vhdl_parser.py 13.9 KB
Newer Older
1
#!/usr/bin/python
2 3
# -*- coding: utf-8 -*-
#
4
# Copyright (c) 2013-2015 CERN
5
# Author:
6 7
#     Tomasz Wlostowski (tomasz.wlostowski@cern.ch)
#     Adrian Fiergolski (Adrian.Fiergolski@cern.ch)
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
#
# This file is part of Hdlmake.
#
# Hdlmake is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Hdlmake is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Hdlmake.  If not, see <http://www.gnu.org/licenses/>.

24 25
"""Module providing the VHDL parser capabilities"""

26
from __future__ import absolute_import
27
import logging
Adrian Fiergolski's avatar
Adrian Fiergolski committed
28
import re
29

30 31
from .new_dep_solver import DepParser

32

33
class VHDLParser(DepParser):
34

35 36
    """Class providing the container for VHDL parser instances"""

Adrian Fiergolski's avatar
Adrian Fiergolski committed
37 38
    def __init__(self, dep_file):
        DepParser.__init__(self, dep_file)
39
        # self.preprocessor = VHDLPreprocessor()
Adrian Fiergolski's avatar
Adrian Fiergolski committed
40

41
    def parse(self, dep_file):
42
        """Parse the provided VHDL file and add the detected relations to it"""
43
        from .dep_file import DepRelation
44 45
        if dep_file.is_parsed:
            return
46
        logging.debug("Parsing %s", dep_file.path)
47

48 49 50 51 52 53 54 55 56
        def _preprocess(vhdl_file):
            """Preprocess the supplied VHDL file instance"""
            def _preprocess_file(file_content, file_name, library):
                """Preprocess the suplied string using the arguments"""
                def _remove_comments_and_strings(text):
                    """Remove the comments and strings from the VHDL code"""
                    pattern = re.compile('--.*?$|".?"',
                                         re.DOTALL | re.MULTILINE)
                    return re.sub(pattern, "", text)
57 58 59
                logging.debug(
                    "preprocess file %s (of length %d) in library %s",
                    file_name, len(file_content), library)
60 61 62 63 64 65 66
                return _remove_comments_and_strings(file_content)
            file_path = vhdl_file.file_path
            buf = open(file_path, "r").read()
            return _preprocess_file(file_content=buf,
                                    file_name=file_path,
                                    library=vhdl_file.library)
        buf = _preprocess(dep_file)
67 68
        # use packages
        use_pattern = re.compile(
69
            r"^\s*use\s+(\w+)\s*\.\s*(\w+)",
70
            re.DOTALL | re.MULTILINE | re.IGNORECASE)
71

72 73 74 75 76 77 78 79
        def do_use(text):
            """Function to be applied by re.sub to every match of the
            use_pattern in the VHDL code -- group() returns positive matches
            as indexed plain strings. It adds the found USE relations to the
            file"""
            if text.group(1).lower() == "work":
                logging.debug("use package %s.%s",
                              dep_file.library, text.group(2))
80
                dep_file.add_relation(
81 82 83
                    DepRelation("%s.%s" % (dep_file.library, text.group(2)),
                                DepRelation.USE,
                                DepRelation.PACKAGE))
84
            else:
85 86
                logging.debug("use package %s.%s",
                              text.group(1), text.group(2))
87
                dep_file.add_relation(
88
                    DepRelation("%s.%s" % (text.group(1), text.group(2)),
89 90
                                DepRelation.USE,
                                DepRelation.PACKAGE))
91 92
            return "<hdlmake use_pattern %s.%s>" % (text.group(1),
                                                    text.group(2))
93
        buf = re.sub(use_pattern, do_use, buf)
94 95
        # new entity
        entity_pattern = re.compile(
96
            r"^\s*entity\s+(?P<name>\w+)\s+is\s+(?:port|generic|end)"
97
            r".*?((?P=name)|entity)\s*;",
98
            re.DOTALL | re.MULTILINE | re.IGNORECASE)
99 100 101 102 103 104 105
        def do_entity(text):
            """Function to be applied by re.sub to every match of the
            entity_pattern in the VHDL code -- group() returns positive matches
            as indexed plain strings. It adds the found PROVIDE relations
            to the file"""
            logging.debug("found entity %s.%s",
                          dep_file.library, text.group(1))
106
            dep_file.add_relation(
107
                DepRelation("%s.%s" % (dep_file.library, text.group(1)),
108 109
                            DepRelation.PROVIDE,
                            DepRelation.ENTITY))
110 111 112 113
            dep_file.add_relation(
                DepRelation("%s.%s" % (dep_file.library, text.group(1)),
                            DepRelation.USE,
                            DepRelation.ARCHITECTURE))
114 115
            return "<hdlmake entity_pattern %s.%s>" % (dep_file.library,
                                                       text.group(1))
116

117
        buf = re.sub(entity_pattern, do_entity, buf)
118

119 120
        # new architecture
        architecture_pattern = re.compile(
121 122 123 124 125
            r"^\s*architecture\s+(?P<name>\w+)\s+of\s+(\w+)\s+is"
            r".*end\s*((|architecture)\s*(?P=name)|architecture)\s*;",
            re.DOTALL | re.MULTILINE | re.IGNORECASE)
        architecture_split_pattern = re.compile(
            r"^\s*architecture\s+(?P<name>\w+)\s+of\s+(\w+)\s+is",
126
            re.DOTALL | re.MULTILINE | re.IGNORECASE)
127

128 129 130 131 132 133 134
        def do_architecture(text):
            """Function to be applied by re.sub to every match of the
            architecture_pattern in the VHDL code -- group() returns positive
            matches as indexed plain strings. It adds the found PROVIDE
            relations to the file"""
            logging.debug("found architecture %s of entity %s.%s",
                          text.group(1), dep_file.library, text.group(2))
135
            dep_file.add_relation(
136
                DepRelation("%s.%s" % (dep_file.library, text.group(2)),
137 138
                            DepRelation.PROVIDE,
                            DepRelation.ARCHITECTURE))
139 140
            return "<hdlmake architecture %s.%s>" % (dep_file.library,
                                                     text.group(2))
141 142
        buf = re.sub(architecture_split_pattern, do_architecture, buf)

143 144
        # new package
        package_pattern = re.compile(
145
            r"^\s*package\s+(\w+)\s+is",
146
            re.DOTALL | re.MULTILINE | re.IGNORECASE)
147

148 149
        def do_package(text):
            """Function to be applied by re.sub to every match of the
150 151 152
            package_pattern in the VHDL code -- group() returns positive
            matches as indexed plain strings. It adds the found PROVIDE
            relations to the file"""
153
            logging.debug("found package %s.%s", dep_file.library,
154
                          text.group(1))
155
            dep_file.add_relation(
156
                DepRelation("%s.%s" % (dep_file.library, text.group(1)),
157 158
                            DepRelation.PROVIDE,
                            DepRelation.PACKAGE))
159 160
            return "<hdlmake package %s.%s>" % (dep_file.library,
                                                text.group(1))
161
        buf = re.sub(package_pattern, do_package, buf)
162

163 164
        # component declaration
        component_pattern = re.compile(
165
            r"^\s*component\s+(\w+).*?end\s+component.*?;",
166
            re.DOTALL | re.MULTILINE | re.IGNORECASE)
167

168 169 170 171 172 173
        def do_component(text):
            """Function to be applied by re.sub to every match of the
            component_pattern in the VHDL code -- group() returns positive
            matches as indexed plain strings. It doesn't add any relation
            to the file"""
            logging.debug("found component declaration %s", text.group(1))
174 175 176 177
            #dep_file.add_relation(
            #    DepRelation("%s.%s" % (dep_file.library, text.group(1)),
            #                DepRelation.USE,
            #                DepRelation.ENTITY))
178
            return "<hdlmake component %s>" % text.group(1)
179

180
        buf = re.sub(component_pattern, do_component, buf)
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212

        # Signal declaration
        signal_pattern = re.compile(
            r"^\s*signal\s+(\w+).*?;",
            re.DOTALL | re.MULTILINE | re.IGNORECASE)

        def do_signal(text):
            """Function to be applied by re.sub to every match of the
            signal_pattern in the VHDL code -- group() returns positive
            matches as indexed plain strings. It doesn't add any relation
            to the file"""
            logging.debug("found signal declaration %s", text.group(1))
            return "<hdlmake signal %s>" % text.group(1)

        buf = re.sub(signal_pattern, do_signal, buf)

        # Constant declaration
        constant_pattern = re.compile(
            r"^\s*constant\s+(\w+).*?;",
            re.DOTALL | re.MULTILINE | re.IGNORECASE)

        def do_constant(text):
            """Function to be applied by re.sub to every match of the
            constant_pattern in the VHDL code -- group() returns positive
            matches as indexed plain strings. It doesn't add any relation
            to the file"""
            logging.debug("found constant declaration %s", text.group(1))
            return "<hdlmake constant %s>" % text.group(1)

        buf = re.sub(constant_pattern, do_constant, buf)


213 214
        # record declaration
        record_pattern = re.compile(
215
            r"^\s*type\s+(\w+)\s+is\s+record.*?end\s+record.*?;",
216
            re.DOTALL | re.MULTILINE | re.IGNORECASE)
217

218 219 220
        def do_record(text):
            """Function to be applied by re.sub to every match of the
            record_pattern in the VHDL code -- group() returns positive matches
221 222
            as indexed plain strings. It doesn't add any relation to the
            file"""
223 224
            logging.debug("found record declaration %s", text.group(1))
            return "<hdlmake record %s>" % text.group(1)
225

226
        buf = re.sub(record_pattern, do_record, buf)
227

228 229
        # function declaration
        function_pattern = re.compile(
230 231 232 233 234
            r"^\s*function\s+(?P<name>\w+)"
            r".*?" # gobble arguments if any.
            r"return\s+\w+"
            r"(\s+is.*?end\s+function.*?)?" # gobble body if any.
            r"\s*;",
235
            re.DOTALL | re.MULTILINE | re.IGNORECASE)
236

237 238 239 240 241 242 243
        def do_function(text):
            """Function to be applied by re.sub to every match of the
            funtion_pattern in the VHDL code -- group() returns positive
            matches as indexed plain strings. It doesn't add the relations
            to the file"""
            logging.debug("found function declaration %s", text.group(1))
            return "<hdlmake function %s>" % text.group(1)
244

245
        buf = re.sub(function_pattern, do_function, buf)
246

247 248
        # instantions
        libraries = set([dep_file.library])
249
        instance_pattern = re.compile(
250
            r"^\s*(\w+)\s*:\s*(?:entity\s+\w+\.)?(\w+)\s*(?:port\s+map.*?|generic\s+map.*?)",
251
            re.DOTALL | re.MULTILINE | re.IGNORECASE)
252

253 254 255 256 257 258 259 260
        def do_instance(text):
            """Function to be applied by re.sub to every match of the
            instance_pattern in the VHDL code -- group() returns positive
            matches as indexed plain strings. It adds the found USE
            relations to the file"""
            for lib in libraries:
                logging.debug("-> instantiates %s.%s as %s",
                              lib, text.group(2), text.group(1))
261 262 263
                dep_file.add_relation(DepRelation(
                    "%s.%s" % (lib, text.group(2)),
                    DepRelation.USE, DepRelation.ENTITY))
264 265 266
            return "<hdlmake instance %s|%s>" % (text.group(1),
                                                 text.group(2))
        buf = re.sub(instance_pattern, do_instance, buf)
267

268
        instance_from_library_pattern = re.compile(
269 270
            r"^\s*(\w+)\s*\:\s*entity\s*(\w+)\s*\.\s*(\w+)\s*(?:port"
            r"\s+map.*?;|generic\s+map.*?;|\s*;)",
271
            re.DOTALL | re.MULTILINE | re.IGNORECASE)
272

273 274 275 276 277 278 279 280
        def do_instance_from_library(text):
            """Function to be applied by re.sub to every match of the
            instance_from_library_pattern in the VHDL code -- group()
            returns positive matches as indexed plain strings.
            It adds the found USE relations to the file"""
            if text.group(2).lower() == "work":
                logging.debug("-> instantiates %s.%s as %s",
                              dep_file.library, text.group(3), text.group(1))
281 282 283 284
                #dep_file.add_relation(
                #    DepRelation("%s.%s" % (dep_file.library, text.group(3)),
                #                DepRelation.USE,
                #                DepRelation.ARCHITECTURE))
285
            else:
286 287
                logging.debug("-> instantiates %s.%s as %s",
                              text.group(2), text.group(3), text.group(1))
288 289 290 291
                #dep_file.add_relation(
                #    DepRelation("%s.%s" % (text.group(2), text.group(3)),
                #                DepRelation.USE,
                #                DepRelation.ARCHITECTURE))
292 293 294 295
            return "<hdlmake instance_from_library %s|%s>" % (text.group(1),
                                                              text.group(3))
        buf = re.sub(instance_from_library_pattern,
                     do_instance_from_library, buf)
296 297
        # libraries
        library_pattern = re.compile(
298
            r"^\s*library\s*(\w+)\s*;",
299
            re.DOTALL | re.MULTILINE | re.IGNORECASE)
300

301 302 303 304 305 306 307 308
        def do_library(text):
            """Function to be applied by re.sub to every match of the
            library_pattern in the VHDL code -- group() returns positive
            matches as indexed plain strings. It adds the used libraries
            to the file's 'library' property"""
            logging.debug("use library %s", text.group(1))
            libraries.add(text.group(1))
            return "<hdlmake library %s>" % text.group(1)
309
        buf = re.sub(library_pattern, do_library, buf)
310
        # logging.debug("\n" + buf) # print modified buffer.
311
        dep_file.is_parsed = True