vhdl_parser.py 13.5 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
        def _preprocess(vhdl_file):
            """Preprocess the supplied VHDL file instance"""
            file_path = vhdl_file.file_path
            buf = open(file_path, "r").read()
52 53 54 55 56 57 58
            logging.debug(
                "preprocess file %s (of length %d) in library %s",
                file_path, len(buf), vhdl_file.library)
            # Remove the comments and strings from the VHDL code
            pattern = re.compile('--.*?$|".?"', re.DOTALL | re.MULTILINE)
            return re.sub(pattern, "", buf)

59
        buf = _preprocess(dep_file)
60 61
        # use packages
        use_pattern = re.compile(
62
            r"^\s*use\s+(\w+)\s*\.\s*(\w+)",
63
            re.DOTALL | re.MULTILINE | re.IGNORECASE)
64

65 66 67 68 69 70 71 72
        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))
73
                dep_file.add_relation(
74 75 76
                    DepRelation("%s.%s" % (dep_file.library, text.group(2)),
                                DepRelation.USE,
                                DepRelation.PACKAGE))
77
            else:
78 79
                logging.debug("use package %s.%s",
                              text.group(1), text.group(2))
80
                dep_file.add_relation(
81
                    DepRelation("%s.%s" % (text.group(1), text.group(2)),
82 83
                                DepRelation.USE,
                                DepRelation.PACKAGE))
84 85
            return "<hdlmake use_pattern %s.%s>" % (text.group(1),
                                                    text.group(2))
86
        buf = re.sub(use_pattern, do_use, buf)
87 88
        # new entity
        entity_pattern = re.compile(
89
            r"^\s*entity\s+(?P<name>\w+)\s+is\s+(?:port|generic|end)"
90
            r".*?((?P=name)|entity)\s*;",
91
            re.DOTALL | re.MULTILINE | re.IGNORECASE)
92 93 94 95 96 97 98
        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))
99
            dep_file.add_relation(
100
                DepRelation("%s.%s" % (dep_file.library, text.group(1)),
101 102
                            DepRelation.PROVIDE,
                            DepRelation.ENTITY))
103 104
            return "<hdlmake entity_pattern %s.%s>" % (dep_file.library,
                                                       text.group(1))
105

106
        buf = re.sub(entity_pattern, do_entity, buf)
107

108
        # new architecture
109 110
        architecture_split_pattern = re.compile(
            r"^\s*architecture\s+(?P<name>\w+)\s+of\s+(\w+)\s+is",
111
            re.DOTALL | re.MULTILINE | re.IGNORECASE)
112

113 114 115 116 117 118 119
        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))
120
            dep_file.add_relation(
121
                DepRelation("%s.%s" % (dep_file.library, text.group(2)),
122 123
                            DepRelation.PROVIDE,
                            DepRelation.ARCHITECTURE))
124 125 126 127 128
            dep_file.add_relation(
                DepRelation("%s.%s" % (dep_file.library, text.group(2)),
                            DepRelation.USE,
                            DepRelation.ENTITY))

129 130
            return "<hdlmake architecture %s.%s>" % (dep_file.library,
                                                     text.group(2))
131 132
        buf = re.sub(architecture_split_pattern, do_architecture, buf)

133 134
        # new package
        package_pattern = re.compile(
135
            r"^\s*package\s+(\w+)\s+is",
136
            re.DOTALL | re.MULTILINE | re.IGNORECASE)
137

138 139
        def do_package(text):
            """Function to be applied by re.sub to every match of the
140 141 142
            package_pattern in the VHDL code -- group() returns positive
            matches as indexed plain strings. It adds the found PROVIDE
            relations to the file"""
143
            logging.debug("found package %s.%s", dep_file.library,
144
                          text.group(1))
145
            dep_file.add_relation(
146
                DepRelation("%s.%s" % (dep_file.library, text.group(1)),
147 148
                            DepRelation.PROVIDE,
                            DepRelation.PACKAGE))
149 150
            return "<hdlmake package %s.%s>" % (dep_file.library,
                                                text.group(1))
151
        buf = re.sub(package_pattern, do_package, buf)
152

153 154
        # component declaration
        component_pattern = re.compile(
155
            r"^\s*component\s+(\w+).*?end\s+component.*?;",
156
            re.DOTALL | re.MULTILINE | re.IGNORECASE)
157

158 159 160 161 162 163
        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))
164 165 166 167
            #dep_file.add_relation(
            #    DepRelation("%s.%s" % (dep_file.library, text.group(1)),
            #                DepRelation.USE,
            #                DepRelation.ENTITY))
168
            return "<hdlmake component %s>" % text.group(1)
169

170
        buf = re.sub(component_pattern, do_component, buf)
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202

        # 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)


203 204
        # record declaration
        record_pattern = re.compile(
205
            r"^\s*type\s+(\w+)\s+is\s+record.*?end\s+record.*?;",
206
            re.DOTALL | re.MULTILINE | re.IGNORECASE)
207

208 209 210
        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
211 212
            as indexed plain strings. It doesn't add any relation to the
            file"""
213 214
            logging.debug("found record declaration %s", text.group(1))
            return "<hdlmake record %s>" % text.group(1)
215

216
        buf = re.sub(record_pattern, do_record, buf)
217

218 219
        # function declaration
        function_pattern = re.compile(
220 221 222 223 224
            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*;",
225
            re.DOTALL | re.MULTILINE | re.IGNORECASE)
226

227 228 229 230 231 232 233
        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)
234

235
        buf = re.sub(function_pattern, do_function, buf)
236

237 238
        # instantions
        libraries = set([dep_file.library])
239
        instance_pattern = re.compile(
240 241 242 243
            r"^\s*(?P<LABEL>\w+)\s*:"
            r"\s*(?:entity\s+(?P<LIB>\w+)\.)?(?P<ENTITY>\w+)"
            r"\s*(?:\(\s*(?P<ARCH>\w+)\s*\)\s*)?"
            r"(?:port\s+map.*?|generic\s+map.*?)",
244
            re.DOTALL | re.MULTILINE | re.IGNORECASE)
245

246 247 248 249 250
        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"""
251 252 253 254 255 256 257 258 259 260 261
            logging.debug("-> instantiates %s.%s(%s) as %s",
                          text.group("LIB"), text.group("ENTITY"), text.group("ARCH"), text.group("LABEL"))
            lib = text.group("LIB")
            if not lib or lib == "work":
                lib = dep_file.library
            dep_file.add_relation(DepRelation(
                "%s.%s" % (lib, text.group("ENTITY")),
                DepRelation.USE, DepRelation.ENTITY))
            return "<hdlmake instance %s|%s|%s>" % (text.group("LABEL"),
                                                    lib,
                                                    text.group("ENTITY"))
262
        buf = re.sub(instance_pattern, do_instance, buf)
263

264
        instance_from_library_pattern = re.compile(
265 266
            r"^\s*(\w+)\s*\:\s*entity\s*(\w+)\s*\.\s*(\w+)\s*(?:port"
            r"\s+map.*?;|generic\s+map.*?;|\s*;)",
267
            re.DOTALL | re.MULTILINE | re.IGNORECASE)
268

269 270 271 272 273 274 275 276
        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))
277 278 279 280
                #dep_file.add_relation(
                #    DepRelation("%s.%s" % (dep_file.library, text.group(3)),
                #                DepRelation.USE,
                #                DepRelation.ARCHITECTURE))
281
            else:
282 283
                logging.debug("-> instantiates %s.%s as %s",
                              text.group(2), text.group(3), text.group(1))
284 285 286 287
                #dep_file.add_relation(
                #    DepRelation("%s.%s" % (text.group(2), text.group(3)),
                #                DepRelation.USE,
                #                DepRelation.ARCHITECTURE))
288 289 290 291
            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)
292 293
        # libraries
        library_pattern = re.compile(
294
            r"^\s*library\s*(\w+)\s*;",
295
            re.DOTALL | re.MULTILINE | re.IGNORECASE)
296

297 298 299 300 301 302 303 304
        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)
305
        buf = re.sub(library_pattern, do_library, buf)
306
        # logging.debug("\n" + buf) # print modified buffer.
307
        dep_file.is_parsed = True