Coverage for pyVHDLModel/Regions.py: 94%
910 statements
« prev ^ index » next coverage.py v7.17.0, created at 2026-10-22 19:31 +0000
« prev ^ index » next coverage.py v7.17.0, created at 2026-10-22 19:31 +0000
1# ==================================================================================================================== #
2# __ ___ _ ____ _ __ __ _ _ #
3# _ __ _ \ \ / / | | | _ \| | | \/ | ___ __| | ___| | #
4# | '_ \| | | \ \ / /| |_| | | | | | | |\/| |/ _ \ / _` |/ _ \ | #
5# | |_) | |_| |\ V / | _ | |_| | |___| | | | (_) | (_| | __/ | #
6# | .__/ \__, | \_/ |_| |_|____/|_____|_| |_|\___/ \__,_|\___|_| #
7# |_| |___/ #
8# ==================================================================================================================== #
9# Authors: #
10# Patrick Lehmann #
11# #
12# License: #
13# ==================================================================================================================== #
14# Copyright 2017-2026 Patrick Lehmann - Boetzingen, Germany #
15# Copyright 2016-2017 Patrick Lehmann - Dresden, Germany #
16# #
17# Licensed under the Apache License, Version 2.0 (the "License"); #
18# you may not use this file except in compliance with the License. #
19# You may obtain a copy of the License at #
20# #
21# http://www.apache.org/licenses/LICENSE-2.0 #
22# #
23# Unless required by applicable law or agreed to in writing, software #
24# distributed under the License is distributed on an "AS IS" BASIS, #
25# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
26# See the License for the specific language governing permissions and #
27# limitations under the License. #
28# #
29# SPDX-License-Identifier: Apache-2.0 #
30# ==================================================================================================================== #
31#
32"""
33This module contains parts of an abstract document language model for VHDL.
35tbd.
36"""
37from __future__ import annotations
39from typing import TYPE_CHECKING, Any, ClassVar, Iterable, Optional as Nullable
41from pyTooling.Common import getFullyQualifiedName, getTypeName
42from pyTooling.Decorators import export, readonly
43from pyTooling.MetaClasses import ExtendedType, expects
44from pyTooling.Warning import WarningCollector
46from pyVHDLModel.Base import ModelEntity, NamedEntityMixin, MultipleNamedEntityMixin, identifiersOf
47from pyVHDLModel.Base import normalizedIdentifiersOf, normalizedDeclaredNamesOf
48from pyVHDLModel.Exception import NotImplementedWarning, AlreadyInDesignError, NotAChildError
49from pyVHDLModel.Exception import DuplicateGenericError, DuplicatePortError, DuplicateParameterError
50from pyVHDLModel.Exception import DuplicateDeclarationError, DuplicateTypeError, DuplicateSubtypeError
51from pyVHDLModel.Exception import DuplicateConstantError, DuplicateSignalError, DuplicateVariableError
52from pyVHDLModel.Exception import DuplicateFileError, DuplicateComponentError, DuplicateSubprogramError
53from pyVHDLModel.Namespace import Namespace
54from pyVHDLModel.Object import BaseConstant, Constant, DeferredConstant, SharedVariable, File, Variable, Signal
55if TYPE_CHECKING: 55 ↛ 56line 55 didn't jump to line 56 because the condition on line 55 was never true
56 from pyVHDLModel.Interface import GenericInterfaceItemMixin, PortInterfaceItemMixin, ParameterInterfaceItemMixin
57 from pyVHDLModel.Interface import PortSignalInterfaceItem
58 from pyVHDLModel.Type import Subtype, FullType
60# `pyVHDLModel.Subprogram` imports this module (Subprogram is a sequential declaration region), so
61# `Function`/`Procedure` are quoted in annotations and imported lazily where they're needed at runtime.
62# The same holds for the interface item classes of `pyVHDLModel.Interface`.
65@export
66class WithGenericsMixin(
67 metaclass=ExtendedType,
68 mixin=True,
69 expects=("_AddToNamespace", "_RemoveFromNamespace", "_ClashingDeclarations")
70):
71 """
72 A mixin-class for language constructs with a generic clause.
74 The host class provides ``_AddToNamespace(element)`` and ``_RemoveFromNamespace(element)``, called when a generic is
75 inserted or removed: :class:`~pyVHDLModel.Regions.DeclarationRegionMixin` updates its namespace, a language construct
76 without a namespace implements both as no-operations.
78 The host class also provides ``_ClashingDeclarations(item)``, called before a generic is inserted: it returns the
79 declarations sharing an identifier with the new item.
81 Indexing the generics (``_IndexGenericItems()``) needs the host's namespace ``_namespace``; a host without one, e.g.
82 a component, can't index them.
84 .. seealso::
86 * :class:`Package <pyVHDLModel.DesignUnit.Package>`
87 * :class:`Entity <pyVHDLModel.DesignUnit.Entity>`
88 * :class:`Component <pyVHDLModel.DesignUnit.Component>`
89 * :class:`Subprogram <pyVHDLModel.Subprogram.Subprogram>`
90 * :class:`Generic group <pyVHDLModel.Interface.GenericGroup>`
91 """
92 _genericItems: list[GenericInterfaceItemMixin] #: List of all generics, in declaration order.
94 def __init__(
95 self,
96 genericItems: Nullable[Iterable[GenericInterfaceItemMixin]] = None,
97 ) -> None:
98 """
99 Initializes a language construct with a generic clause.
101 :param genericItems: List of all generics, in declaration order.
102 """
103 self._genericItems = []
104 if genericItems is not None:
105 for item in genericItems:
106 self._genericItems.append(item)
107 item.Parent = self
109 @readonly
110 def GenericItems(self) -> list[GenericInterfaceItemMixin]:
111 """
112 Read-only property to access the generic items (:attr:`_genericItems`).
114 :returns: List of generic items.
115 """
116 return self._genericItems
118 @readonly
119 def GenericCount(self) -> int:
120 """
121 Read-only property to return the number of generics in :attr:`_genericItems`.
123 :returns: The generic count.
124 """
125 return len(self._genericItems)
127 def AddGenericItem(self, genericItem: GenericInterfaceItemMixin) -> None:
128 """
129 Add a generic at the end of the generic clause.
131 The generic's parent is set to this language construct, and its identifiers are added to the language construct's
132 namespace, if it has one.
134 :param genericItem: The generic to add.
135 :raises ValueError: If parameter 'genericItem' is ``None``.
136 :raises TypeError: If parameter 'genericItem' is not of type
137 :class:`~pyVHDLModel.Interface.GenericInterfaceItemMixin`.
138 :raises AlreadyInDesignError: If parameter 'genericItem' already has a parent.
139 :raises DuplicateGenericError: If an existing declaration of the language construct shares an identifier
140 with the generic.
142 .. seealso::
144 :meth:`InsertGenericItem`
145 |rarr| Insert a generic at a position of the generic clause.
146 :meth:`RemoveGenericItem`
147 |rarr| Remove a generic from the generic clause.
148 """
149 self.InsertGenericItem(len(self._genericItems), genericItem)
151 def InsertGenericItem(self, index: int, genericItem: GenericInterfaceItemMixin) -> None:
152 """
153 Insert a generic at a position of the generic clause.
155 The generic's parent is set to this language construct, and its identifiers are added to the language construct's
156 namespace, if it has one.
158 :param index: Position, from ``0`` (first) to :data:`GenericCount` (append).
159 A negative index counts from the end, as for :meth:`list.insert`: ``-1`` inserts
160 before the last generic.
161 :param genericItem: The generic to insert.
162 :raises ValueError: If parameter 'index' is ``None``.
163 :raises TypeError: If parameter 'index' is not of type :class:`int`.
164 :raises ValueError: If parameter 'index' is out of range.
165 :raises ValueError: If parameter 'genericItem' is ``None``.
166 :raises TypeError: If parameter 'genericItem' is not of type
167 :class:`~pyVHDLModel.Interface.GenericInterfaceItemMixin`.
168 :raises AlreadyInDesignError: If parameter 'genericItem' already has a parent.
169 :raises DuplicateGenericError: If an existing declaration of the language construct shares an identifier
170 with the generic.
172 .. seealso::
174 :meth:`AddGenericItem`
175 |rarr| Add a generic at the end of the generic clause.
176 :meth:`RemoveGenericItem`
177 |rarr| Remove a generic from the generic clause.
178 :meth:`MoveGenericItem`
179 |rarr| Move a generic to a position of the generic clause.
180 """
181 from pyVHDLModel.Interface import GenericInterfaceItemMixin
183 if index is None:
184 raise ValueError("Parameter 'index' is None.")
185 elif not isinstance(index, int):
186 ex = TypeError("Parameter 'index' is not of type 'int'.")
187 ex.add_note(f"Got type '{getFullyQualifiedName(index)}'.")
188 raise ex
189 elif not (-len(self._genericItems) <= index <= len(self._genericItems)):
190 ex = ValueError(f"Parameter 'index' is out of range {-len(self._genericItems)}..{len(self._genericItems)}.")
191 ex.add_note(f"Got value '{index}'.")
192 raise ex
194 if genericItem is None:
195 raise ValueError("Parameter 'genericItem' is None.")
196 elif not isinstance(genericItem, GenericInterfaceItemMixin):
197 ex = TypeError("Parameter 'genericItem' is not of type 'GenericInterfaceItemMixin'.")
198 ex.add_note(f"Got type '{getFullyQualifiedName(genericItem)}'.")
199 raise ex
200 elif genericItem._parent is not None:
201 identifiers = ", ".join(identifiersOf(genericItem))
202 ex = AlreadyInDesignError(
203 f"{getTypeName(genericItem)} '{identifiers}' already has a parent.",
204 element=genericItem,
205 parent=genericItem._parent
206 )
207 ex.add_note(f"Remove it from its {getTypeName(genericItem._parent)} first.")
208 raise ex
210 if len(clashes := self._ClashingDeclarations(genericItem)) > 0:
211 existingIdentifiers = normalizedIdentifiersOf(clashes[0])
212 newIdentifiers = zip(identifiersOf(genericItem), normalizedIdentifiersOf(genericItem))
213 identifier = next(name for name, normalizedName in newIdentifiers if normalizedName in existingIdentifiers)
214 ex = DuplicateGenericError(
215 f"{getTypeName(clashes[0]._parent)} already declares '{identifier}'.",
216 identifier=identifier,
217 parent=self,
218 element=genericItem,
219 existing=clashes[0]
220 )
221 ex.add_note(f"Existing declaration is of type '{getFullyQualifiedName(clashes[0])}'.")
222 raise ex
224 self._genericItems.insert(index, genericItem)
225 genericItem.Parent = self
226 self._AddToNamespace(genericItem)
228 def RemoveGenericItem(self, genericItem: GenericInterfaceItemMixin) -> None:
229 """
230 Remove a generic from the generic clause.
232 The generic is detached: its parent is cleared and its identifiers are removed from the language construct's
233 namespace, if it has one. The generic itself is unchanged and can be added to another language construct, or
234 inserted at another position.
236 :param genericItem: The generic to remove.
237 :raises ValueError: If parameter 'genericItem' is ``None``.
238 :raises TypeError: If parameter 'genericItem' is not of type
239 :class:`~pyVHDLModel.Interface.GenericInterfaceItemMixin`.
240 :raises NotAChildError: If parameter 'genericItem' is not a generic of this language construct.
241 :raises NotInNamespaceError: If the namespace is indexed, and an identifier of the generic isn't declared in it.
242 :raises NotInNamespaceError: If the namespace is indexed, and an identifier of the generic refers to another
243 element, e.g. a duplicate declaration.
245 .. seealso::
247 :meth:`AddGenericItem`
248 |rarr| Add a generic at the end of the generic clause.
249 :meth:`InsertGenericItem`
250 |rarr| Insert a generic at a position of the generic clause.
251 :meth:`MoveGenericItem`
252 |rarr| Move a generic to a position of the generic clause.
253 """
254 from pyVHDLModel.Interface import GenericInterfaceItemMixin
256 if genericItem is None:
257 raise ValueError("Parameter 'genericItem' is None.")
258 elif not isinstance(genericItem, GenericInterfaceItemMixin):
259 ex = TypeError("Parameter 'genericItem' is not of type 'GenericInterfaceItemMixin'.")
260 ex.add_note(f"Got type '{getFullyQualifiedName(genericItem)}'.")
261 raise ex
263 for index, item in enumerate(self._genericItems):
264 if item is genericItem:
265 break
266 else:
267 identifiers = ", ".join(identifiersOf(genericItem))
268 raise NotAChildError(
269 f"{getTypeName(genericItem)} '{identifiers}' is not a generic of this {getTypeName(self)}.",
270 element=genericItem,
271 parent=self
272 )
274 self._RemoveFromNamespace(genericItem)
275 del self._genericItems[index]
276 genericItem._parent = None
278 def MoveGenericItem(self, genericItem: GenericInterfaceItemMixin, index: int) -> None:
279 """
280 Move a generic to a position of the generic clause.
282 The generic stays a generic of this language construct: only the order of the generic clause changes. A generic
283 subprogram is an overload: if the language construct's namespace is indexed, it refers to the overload declared
284 last afterwards.
286 :param genericItem: The generic to move.
287 :param index: Position of the generic after the move, from ``0`` (first) to :data:`GenericCount`
288 minus one (last). A negative index counts from the end, as for a :class:`list`
289 index: ``-1`` moves the generic to the end.
290 :raises ValueError: If parameter 'genericItem' is ``None``.
291 :raises TypeError: If parameter 'genericItem' is not of type
292 :class:`~pyVHDLModel.Interface.GenericInterfaceItemMixin`.
293 :raises NotAChildError: If parameter 'genericItem' is not a generic of this language construct.
294 :raises ValueError: If parameter 'index' is ``None``.
295 :raises TypeError: If parameter 'index' is not of type :class:`int`.
296 :raises ValueError: If parameter 'index' is out of range.
297 :raises NotInNamespaceError: If the namespace is indexed, and the identifier of a generic subprogram refers to an
298 element, which is no overload, e.g. a duplicate declaration.
300 .. seealso::
302 :meth:`InsertGenericItem`
303 |rarr| Insert a generic at a position of the generic clause.
304 :meth:`RemoveGenericItem`
305 |rarr| Remove a generic from the generic clause.
306 """
307 from pyVHDLModel.Interface import GenericInterfaceItemMixin
308 from pyVHDLModel.Subprogram import Function, Procedure
310 if genericItem is None:
311 raise ValueError("Parameter 'genericItem' is None.")
312 elif not isinstance(genericItem, GenericInterfaceItemMixin):
313 ex = TypeError("Parameter 'genericItem' is not of type 'GenericInterfaceItemMixin'.")
314 ex.add_note(f"Got type '{getFullyQualifiedName(genericItem)}'.")
315 raise ex
317 for currentIndex, item in enumerate(self._genericItems):
318 if item is genericItem:
319 break
320 else:
321 identifiers = ", ".join(identifiersOf(genericItem))
322 raise NotAChildError(
323 f"{getTypeName(genericItem)} '{identifiers}' is not a generic of this {getTypeName(self)}.",
324 element=genericItem,
325 parent=self
326 )
328 if index is None:
329 raise ValueError("Parameter 'index' is None.")
330 elif not isinstance(index, int):
331 ex = TypeError("Parameter 'index' is not of type 'int'.")
332 ex.add_note(f"Got type '{getFullyQualifiedName(index)}'.")
333 raise ex
334 elif not (-len(self._genericItems) <= index < len(self._genericItems)):
335 ex = ValueError(f"Parameter 'index' is out of range {-len(self._genericItems)}..{len(self._genericItems) - 1}.")
336 ex.add_note(f"Got value '{index}'.")
337 raise ex
339 newIndex = index if index >= 0 else len(self._genericItems) + index
340 if newIndex == currentIndex:
341 return
343 isOverload = isinstance(genericItem, (Function, Procedure))
344 if isOverload:
345 self._RemoveFromNamespace(genericItem)
347 del self._genericItems[currentIndex]
348 self._genericItems.insert(newIndex, genericItem)
350 if isOverload:
351 self._AddToNamespace(genericItem)
353 @expects("_namespace")
354 def _IndexGenericItems(self) -> None:
355 """Add the generics to the host's namespace; a generic subprogram is an overload."""
356 from pyVHDLModel.Subprogram import Function, Procedure
358 for item in self._genericItems:
359 overloadable = isinstance(item, (Function, Procedure))
360 for normalizedIdentifier in normalizedIdentifiersOf(item):
361 self._namespace.AddElement(normalizedIdentifier, item, overloadable=overloadable)
364@export
365class WithPortsMixin(
366 metaclass=ExtendedType,
367 mixin=True,
368 expects=("_AddToNamespace", "_RemoveFromNamespace", "_ClashingDeclarations")
369):
370 """
371 A mixin-class for language constructs with a port clause.
373 The host class provides ``_AddToNamespace(element)`` and ``_RemoveFromNamespace(element)``, called when a port is
374 inserted or removed: :class:`~pyVHDLModel.Regions.DeclarationRegionMixin` updates its namespace, a language construct
375 without a namespace implements both as no-operations.
377 The host class also provides ``_ClashingDeclarations(item)``, called before a port is inserted: it returns the
378 declarations sharing an identifier with the new item.
380 Indexing the ports (``_IndexPortItems()``) needs the host's namespace ``_namespace``; a host without one, e.g.
381 a component, can't index them.
383 .. seealso::
385 * :class:`Concurrent block statement <pyVHDLModel.Concurrent.ConcurrentBlockStatement>`
386 * :class:`Entity <pyVHDLModel.DesignUnit.Entity>`
387 * :class:`Component <pyVHDLModel.DesignUnit.Component>`
388 * :class:`Port group <pyVHDLModel.Interface.PortGroup>`
389 """
390 _portItems: list[PortInterfaceItemMixin] #: List of all ports, in declaration order.
392 def __init__(
393 self,
394 portItems: Nullable[Iterable[PortInterfaceItemMixin]] = None,
395 ) -> None:
396 """
397 Initializes a language construct with a port clause.
399 :param portItems: List of all ports, in declaration order.
400 """
401 self._portItems = []
402 if portItems is not None:
403 for item in portItems:
404 self._portItems.append(item)
405 item.Parent = self
407 @readonly
408 def PortItems(self) -> list[PortInterfaceItemMixin]:
409 """
410 Read-only property to access the port items (:attr:`_portItems`).
412 :returns: List of port items.
413 """
414 return self._portItems
416 @readonly
417 def PortCount(self) -> int:
418 """
419 Read-only property to return the number of ports in :attr:`_portItems`.
421 :returns: The port count.
422 """
423 return len(self._portItems)
425 def AddPortItem(self, portItem: PortSignalInterfaceItem) -> None:
426 """
427 Add a port at the end of the port clause.
429 The port's parent is set to this language construct, and its identifiers are added to the language construct's
430 namespace, if it has one.
432 :param portItem: The port to add.
433 :raises ValueError: If parameter 'portItem' is ``None``.
434 :raises TypeError: If parameter 'portItem' is not of type
435 :class:`~pyVHDLModel.Interface.PortSignalInterfaceItem`.
436 :raises AlreadyInDesignError: If parameter 'portItem' already has a parent.
437 :raises DuplicatePortError: If an existing declaration of the language construct shares an identifier
438 with the port.
440 .. seealso::
442 :meth:`InsertPortItem`
443 |rarr| Insert a port at a position of the port clause.
444 :meth:`RemovePortItem`
445 |rarr| Remove a port from the port clause.
446 """
447 self.InsertPortItem(len(self._portItems), portItem)
449 def InsertPortItem(self, index: int, portItem: PortSignalInterfaceItem) -> None:
450 """
451 Insert a port at a position of the port clause.
453 The port's parent is set to this language construct, and its identifiers are added to the language construct's
454 namespace, if it has one.
456 :param index: Position, from ``0`` (first) to :data:`PortCount` (append).
457 A negative index counts from the end, as for :meth:`list.insert`: ``-1`` inserts
458 before the last port.
459 :param portItem: The port to insert.
460 :raises ValueError: If parameter 'index' is ``None``.
461 :raises TypeError: If parameter 'index' is not of type :class:`int`.
462 :raises ValueError: If parameter 'index' is out of range.
463 :raises ValueError: If parameter 'portItem' is ``None``.
464 :raises TypeError: If parameter 'portItem' is not of type
465 :class:`~pyVHDLModel.Interface.PortSignalInterfaceItem`.
466 :raises AlreadyInDesignError: If parameter 'portItem' already has a parent.
467 :raises DuplicatePortError: If an existing declaration of the language construct shares an identifier
468 with the port.
470 .. seealso::
472 :meth:`AddPortItem`
473 |rarr| Add a port at the end of the port clause.
474 :meth:`RemovePortItem`
475 |rarr| Remove a port from the port clause.
476 :meth:`MovePortItem`
477 |rarr| Move a port to a position of the port clause.
478 """
479 from pyVHDLModel.Interface import PortSignalInterfaceItem
481 if index is None: 481 ↛ 482line 481 didn't jump to line 482 because the condition on line 481 was never true
482 raise ValueError("Parameter 'index' is None.")
483 elif not isinstance(index, int): 483 ↛ 484line 483 didn't jump to line 484 because the condition on line 483 was never true
484 ex = TypeError("Parameter 'index' is not of type 'int'.")
485 ex.add_note(f"Got type '{getFullyQualifiedName(index)}'.")
486 raise ex
487 elif not (-len(self._portItems) <= index <= len(self._portItems)):
488 ex = ValueError(f"Parameter 'index' is out of range {-len(self._portItems)}..{len(self._portItems)}.")
489 ex.add_note(f"Got value '{index}'.")
490 raise ex
492 if portItem is None:
493 raise ValueError("Parameter 'portItem' is None.")
494 elif not isinstance(portItem, PortSignalInterfaceItem):
495 ex = TypeError("Parameter 'portItem' is not of type 'PortSignalInterfaceItem'.")
496 ex.add_note(f"Got type '{getFullyQualifiedName(portItem)}'.")
497 raise ex
498 elif portItem._parent is not None:
499 identifiers = ", ".join(identifiersOf(portItem))
500 ex = AlreadyInDesignError(
501 f"{getTypeName(portItem)} '{identifiers}' already has a parent.",
502 element=portItem,
503 parent=portItem._parent
504 )
505 ex.add_note(f"Remove it from its {getTypeName(portItem._parent)} first.")
506 raise ex
508 if len(clashes := self._ClashingDeclarations(portItem)) > 0:
509 existingIdentifiers = normalizedIdentifiersOf(clashes[0])
510 newIdentifiers = zip(identifiersOf(portItem), normalizedIdentifiersOf(portItem))
511 identifier = next(name for name, normalizedName in newIdentifiers if normalizedName in existingIdentifiers)
512 ex = DuplicatePortError(
513 f"{getTypeName(clashes[0]._parent)} already declares '{identifier}'.",
514 identifier=identifier,
515 parent=self,
516 element=portItem,
517 existing=clashes[0]
518 )
519 ex.add_note(f"Existing declaration is of type '{getFullyQualifiedName(clashes[0])}'.")
520 raise ex
522 self._portItems.insert(index, portItem)
523 portItem.Parent = self
524 self._AddToNamespace(portItem)
526 def RemovePortItem(self, portItem: PortSignalInterfaceItem) -> None:
527 """
528 Remove a port from the port clause.
530 The port is detached: its parent is cleared and its identifiers are removed from the language construct's
531 namespace, if it has one. The port itself is unchanged and can be added to another language construct, or
532 inserted at another position.
534 :param portItem: The port to remove.
535 :raises ValueError: If parameter 'portItem' is ``None``.
536 :raises TypeError: If parameter 'portItem' is not of type
537 :class:`~pyVHDLModel.Interface.PortSignalInterfaceItem`.
538 :raises NotAChildError: If parameter 'portItem' is not a port of this language construct.
539 :raises NotInNamespaceError: If the namespace is indexed, and an identifier of the port isn't declared in it.
540 :raises NotInNamespaceError: If the namespace is indexed, and an identifier of the port refers to another
541 element, e.g. a duplicate declaration.
543 .. seealso::
545 :meth:`AddPortItem`
546 |rarr| Add a port at the end of the port clause.
547 :meth:`InsertPortItem`
548 |rarr| Insert a port at a position of the port clause.
549 :meth:`MovePortItem`
550 |rarr| Move a port to a position of the port clause.
551 """
552 from pyVHDLModel.Interface import PortSignalInterfaceItem
554 if portItem is None:
555 raise ValueError("Parameter 'portItem' is None.")
556 elif not isinstance(portItem, PortSignalInterfaceItem):
557 ex = TypeError("Parameter 'portItem' is not of type 'PortSignalInterfaceItem'.")
558 ex.add_note(f"Got type '{getFullyQualifiedName(portItem)}'.")
559 raise ex
561 for index, item in enumerate(self._portItems):
562 if item is portItem: 562 ↛ 561line 562 didn't jump to line 561 because the condition on line 562 was always true
563 break
564 else:
565 identifiers = ", ".join(identifiersOf(portItem))
566 raise NotAChildError(
567 f"{getTypeName(portItem)} '{identifiers}' is not a port of this {getTypeName(self)}.",
568 element=portItem,
569 parent=self
570 )
572 self._RemoveFromNamespace(portItem)
573 del self._portItems[index]
574 portItem._parent = None
576 def MovePortItem(self, portItem: PortSignalInterfaceItem, index: int) -> None:
577 """
578 Move a port to a position of the port clause.
580 The port stays a port of this language construct: only the order of the port clause changes.
582 :param portItem: The port to move.
583 :param index: Position of the port after the move, from ``0`` (first) to :data:`PortCount` minus one
584 (last). A negative index counts from the end, as for a :class:`list` index: ``-1`` moves the
585 port to the end.
586 :raises ValueError: If parameter 'portItem' is ``None``.
587 :raises TypeError: If parameter 'portItem' is not of type
588 :class:`~pyVHDLModel.Interface.PortSignalInterfaceItem`.
589 :raises NotAChildError: If parameter 'portItem' is not a port of this language construct.
590 :raises ValueError: If parameter 'index' is ``None``.
591 :raises TypeError: If parameter 'index' is not of type :class:`int`.
592 :raises ValueError: If parameter 'index' is out of range.
594 .. seealso::
596 :meth:`InsertPortItem`
597 |rarr| Insert a port at a position of the port clause.
598 :meth:`RemovePortItem`
599 |rarr| Remove a port from the port clause.
600 """
601 from pyVHDLModel.Interface import PortSignalInterfaceItem
603 if portItem is None:
604 raise ValueError("Parameter 'portItem' is None.")
605 elif not isinstance(portItem, PortSignalInterfaceItem):
606 ex = TypeError("Parameter 'portItem' is not of type 'PortSignalInterfaceItem'.")
607 ex.add_note(f"Got type '{getFullyQualifiedName(portItem)}'.")
608 raise ex
610 for currentIndex, item in enumerate(self._portItems):
611 if item is portItem:
612 break
613 else:
614 identifiers = ", ".join(identifiersOf(portItem))
615 raise NotAChildError(
616 f"{getTypeName(portItem)} '{identifiers}' is not a port of this {getTypeName(self)}.",
617 element=portItem,
618 parent=self
619 )
621 if index is None: 621 ↛ 622line 621 didn't jump to line 622 because the condition on line 621 was never true
622 raise ValueError("Parameter 'index' is None.")
623 elif not isinstance(index, int): 623 ↛ 624line 623 didn't jump to line 624 because the condition on line 623 was never true
624 ex = TypeError("Parameter 'index' is not of type 'int'.")
625 ex.add_note(f"Got type '{getFullyQualifiedName(index)}'.")
626 raise ex
627 elif not (-len(self._portItems) <= index < len(self._portItems)):
628 ex = ValueError(f"Parameter 'index' is out of range {-len(self._portItems)}..{len(self._portItems) - 1}.")
629 ex.add_note(f"Got value '{index}'.")
630 raise ex
632 newIndex = index if index >= 0 else len(self._portItems) + index
633 del self._portItems[currentIndex]
634 self._portItems.insert(newIndex, portItem)
636 @expects("_namespace")
637 def _IndexPortItems(self) -> None:
638 """Add the ports to the host's namespace."""
639 for item in self._portItems:
640 for normalizedIdentifier in normalizedIdentifiersOf(item):
641 self._namespace.AddElement(normalizedIdentifier, item)
644@export
645class WithParametersMixin(
646 metaclass=ExtendedType,
647 mixin=True,
648 expects=("_AddToNamespace", "_RemoveFromNamespace", "_ClashingDeclarations")
649):
650 """
651 A mixin-class for language constructs with a parameter list.
653 The host class provides ``_AddToNamespace(element)`` and ``_RemoveFromNamespace(element)``, called when a parameter is
654 inserted or removed: :class:`~pyVHDLModel.Regions.DeclarationRegionMixin` updates its namespace, a language construct
655 without a namespace implements both as no-operations.
657 The host class also provides ``_ClashingDeclarations(item)``, called before a parameter is inserted: it returns the
658 declarations sharing an identifier with the new item.
660 Indexing the parameters (``_IndexParameterItems()``) needs the host's namespace ``_namespace``; a host without one,
661 e.g. a parameter group, can't index them.
663 .. seealso::
665 * :class:`Subprogram <pyVHDLModel.Subprogram.Subprogram>`
666 * :class:`Parameter group <pyVHDLModel.Interface.ParameterGroup>`
667 """
668 _parameterItems: list[ParameterInterfaceItemMixin] #: List of all parameters, in declaration order.
670 def __init__(
671 self,
672 parameterItems: Nullable[Iterable[ParameterInterfaceItemMixin]] = None,
673 ) -> None:
674 """
675 Initializes a language construct with a parameter list.
677 :param parameterItems: List of all parameters, in declaration order.
678 """
679 self._parameterItems = []
680 if parameterItems is not None:
681 for item in parameterItems:
682 self._parameterItems.append(item)
683 item.Parent = self
685 @readonly
686 def ParameterItems(self) -> list[ParameterInterfaceItemMixin]:
687 """
688 Read-only property to access the parameter items (:attr:`_parameterItems`).
690 :returns: List of parameter items.
691 """
692 return self._parameterItems
694 @readonly
695 def ParameterCount(self) -> int:
696 """
697 Read-only property to return the number of parameters in :attr:`_parameterItems`.
699 :returns: The parameter count.
700 """
701 return len(self._parameterItems)
703 def AddParameterItem(self, parameterItem: ParameterInterfaceItemMixin) -> None:
704 """
705 Add a parameter at the end of the parameter list.
707 The parameter's parent is set to this language construct, and its identifiers are added to the language construct's
708 namespace, if it has one.
710 :param parameterItem: The parameter to add.
711 :raises ValueError: If parameter 'parameterItem' is ``None``.
712 :raises TypeError: If parameter 'parameterItem' is not of type
713 :class:`~pyVHDLModel.Interface.ParameterInterfaceItemMixin`.
714 :raises AlreadyInDesignError: If parameter 'parameterItem' already has a parent.
715 :raises DuplicateParameterError: If an existing declaration of the language construct shares an identifier
716 with the parameter.
718 .. seealso::
720 :meth:`InsertParameterItem`
721 |rarr| Insert a parameter at a position of the parameter list.
722 :meth:`RemoveParameterItem`
723 |rarr| Remove a parameter from the parameter list.
724 """
725 self.InsertParameterItem(len(self._parameterItems), parameterItem)
727 def InsertParameterItem(self, index: int, parameterItem: ParameterInterfaceItemMixin) -> None:
728 """
729 Insert a parameter at a position of the parameter list.
731 The parameter's parent is set to this language construct, and its identifiers are added to the language construct's
732 namespace, if it has one.
734 :param index: Position, from ``0`` (first) to :data:`ParameterCount` (append).
735 A negative index counts from the end, as for :meth:`list.insert`: ``-1`` inserts
736 before the last parameter.
737 :param parameterItem: The parameter to insert.
738 :raises ValueError: If parameter 'index' is ``None``.
739 :raises TypeError: If parameter 'index' is not of type :class:`int`.
740 :raises ValueError: If parameter 'index' is out of range.
741 :raises ValueError: If parameter 'parameterItem' is ``None``.
742 :raises TypeError: If parameter 'parameterItem' is not of type
743 :class:`~pyVHDLModel.Interface.ParameterInterfaceItemMixin`.
744 :raises AlreadyInDesignError: If parameter 'parameterItem' already has a parent.
745 :raises DuplicateParameterError: If an existing declaration of the language construct shares an identifier
746 with the parameter.
748 .. seealso::
750 :meth:`AddParameterItem`
751 |rarr| Add a parameter at the end of the parameter list.
752 :meth:`RemoveParameterItem`
753 |rarr| Remove a parameter from the parameter list.
754 :meth:`MoveParameterItem`
755 |rarr| Move a parameter to a position of the parameter list.
756 """
757 from pyVHDLModel.Interface import ParameterInterfaceItemMixin
759 if index is None: 759 ↛ 760line 759 didn't jump to line 760 because the condition on line 759 was never true
760 raise ValueError("Parameter 'index' is None.")
761 elif not isinstance(index, int): 761 ↛ 762line 761 didn't jump to line 762 because the condition on line 761 was never true
762 ex = TypeError("Parameter 'index' is not of type 'int'.")
763 ex.add_note(f"Got type '{getFullyQualifiedName(index)}'.")
764 raise ex
765 elif not (-len(self._parameterItems) <= index <= len(self._parameterItems)):
766 ex = ValueError(f"Parameter 'index' is out of range {-len(self._parameterItems)}..{len(self._parameterItems)}.")
767 ex.add_note(f"Got value '{index}'.")
768 raise ex
770 if parameterItem is None:
771 raise ValueError("Parameter 'parameterItem' is None.")
772 elif not isinstance(parameterItem, ParameterInterfaceItemMixin):
773 ex = TypeError("Parameter 'parameterItem' is not of type 'ParameterInterfaceItemMixin'.")
774 ex.add_note(f"Got type '{getFullyQualifiedName(parameterItem)}'.")
775 raise ex
776 elif parameterItem._parent is not None:
777 identifiers = ", ".join(identifiersOf(parameterItem))
778 ex = AlreadyInDesignError(
779 f"{getTypeName(parameterItem)} '{identifiers}' already has a parent.",
780 element=parameterItem,
781 parent=parameterItem._parent
782 )
783 ex.add_note(f"Remove it from its {getTypeName(parameterItem._parent)} first.")
784 raise ex
786 if len(clashes := self._ClashingDeclarations(parameterItem)) > 0:
787 existingIdentifiers = normalizedIdentifiersOf(clashes[0])
788 newIdentifiers = zip(identifiersOf(parameterItem), normalizedIdentifiersOf(parameterItem))
789 identifier = next(name for name, normalizedName in newIdentifiers if normalizedName in existingIdentifiers)
790 ex = DuplicateParameterError(
791 f"{getTypeName(clashes[0]._parent)} already declares '{identifier}'.",
792 identifier=identifier,
793 parent=self,
794 element=parameterItem,
795 existing=clashes[0]
796 )
797 ex.add_note(f"Existing declaration is of type '{getFullyQualifiedName(clashes[0])}'.")
798 raise ex
800 self._parameterItems.insert(index, parameterItem)
801 parameterItem.Parent = self
802 self._AddToNamespace(parameterItem)
804 def RemoveParameterItem(self, parameterItem: ParameterInterfaceItemMixin) -> None:
805 """
806 Remove a parameter from the parameter list.
808 The parameter is detached: its parent is cleared and its identifiers are removed from the language construct's
809 namespace, if it has one. The parameter itself is unchanged and can be added to another language construct, or
810 inserted at another position.
812 :param parameterItem: The parameter to remove.
813 :raises ValueError: If parameter 'parameterItem' is ``None``.
814 :raises TypeError: If parameter 'parameterItem' is not of type
815 :class:`~pyVHDLModel.Interface.ParameterInterfaceItemMixin`.
816 :raises NotAChildError: If parameter 'parameterItem' is not a parameter of this language construct.
817 :raises NotInNamespaceError: If the namespace is indexed, and an identifier of the parameter isn't declared in it.
818 :raises NotInNamespaceError: If the namespace is indexed, and an identifier of the parameter refers to another
819 element, e.g. a duplicate declaration.
821 .. seealso::
823 :meth:`AddParameterItem`
824 |rarr| Add a parameter at the end of the parameter list.
825 :meth:`InsertParameterItem`
826 |rarr| Insert a parameter at a position of the parameter list.
827 :meth:`MoveParameterItem`
828 |rarr| Move a parameter to a position of the parameter list.
829 """
830 from pyVHDLModel.Interface import ParameterInterfaceItemMixin
832 if parameterItem is None:
833 raise ValueError("Parameter 'parameterItem' is None.")
834 elif not isinstance(parameterItem, ParameterInterfaceItemMixin):
835 ex = TypeError("Parameter 'parameterItem' is not of type 'ParameterInterfaceItemMixin'.")
836 ex.add_note(f"Got type '{getFullyQualifiedName(parameterItem)}'.")
837 raise ex
839 for index, item in enumerate(self._parameterItems):
840 if item is parameterItem:
841 break
842 else:
843 identifiers = ", ".join(identifiersOf(parameterItem))
844 raise NotAChildError(
845 f"{getTypeName(parameterItem)} '{identifiers}' is not a parameter of this {getTypeName(self)}.",
846 element=parameterItem,
847 parent=self
848 )
850 self._RemoveFromNamespace(parameterItem)
851 del self._parameterItems[index]
852 parameterItem._parent = None
854 def MoveParameterItem(self, parameterItem: ParameterInterfaceItemMixin, index: int) -> None:
855 """
856 Move a parameter to a position of the parameter list.
858 The parameter stays a parameter of this language construct: only the order of the parameter list changes.
860 :param parameterItem: The parameter to move.
861 :param index: Position of the parameter after the move, from ``0`` (first) to :data:`ParameterCount`
862 minus one (last). A negative index counts from the end, as for a :class:`list` index:
863 ``-1`` moves the parameter to the end.
864 :raises ValueError: If parameter 'parameterItem' is ``None``.
865 :raises TypeError: If parameter 'parameterItem' is not of type
866 :class:`~pyVHDLModel.Interface.ParameterInterfaceItemMixin`.
867 :raises NotAChildError: If parameter 'parameterItem' is not a parameter of this language construct.
868 :raises ValueError: If parameter 'index' is ``None``.
869 :raises TypeError: If parameter 'index' is not of type :class:`int`.
870 :raises ValueError: If parameter 'index' is out of range.
872 .. seealso::
874 :meth:`InsertParameterItem`
875 |rarr| Insert a parameter at a position of the parameter list.
876 :meth:`RemoveParameterItem`
877 |rarr| Remove a parameter from the parameter list.
878 """
879 from pyVHDLModel.Interface import ParameterInterfaceItemMixin
881 if parameterItem is None:
882 raise ValueError("Parameter 'parameterItem' is None.")
883 elif not isinstance(parameterItem, ParameterInterfaceItemMixin):
884 ex = TypeError("Parameter 'parameterItem' is not of type 'ParameterInterfaceItemMixin'.")
885 ex.add_note(f"Got type '{getFullyQualifiedName(parameterItem)}'.")
886 raise ex
888 for currentIndex, item in enumerate(self._parameterItems):
889 if item is parameterItem:
890 break
891 else:
892 identifiers = ", ".join(identifiersOf(parameterItem))
893 raise NotAChildError(
894 f"{getTypeName(parameterItem)} '{identifiers}' is not a parameter of this {getTypeName(self)}.",
895 element=parameterItem,
896 parent=self
897 )
899 if index is None: 899 ↛ 900line 899 didn't jump to line 900 because the condition on line 899 was never true
900 raise ValueError("Parameter 'index' is None.")
901 elif not isinstance(index, int): 901 ↛ 902line 901 didn't jump to line 902 because the condition on line 901 was never true
902 ex = TypeError("Parameter 'index' is not of type 'int'.")
903 ex.add_note(f"Got type '{getFullyQualifiedName(index)}'.")
904 raise ex
905 elif not (-len(self._parameterItems) <= index < len(self._parameterItems)):
906 ex = ValueError(
907 f"Parameter 'index' is out of range {-len(self._parameterItems)}..{len(self._parameterItems) - 1}."
908 )
909 ex.add_note(f"Got value '{index}'.")
910 raise ex
912 newIndex = index if index >= 0 else len(self._parameterItems) + index
913 del self._parameterItems[currentIndex]
914 self._parameterItems.insert(newIndex, parameterItem)
916 @expects("_namespace")
917 def _IndexParameterItems(self) -> None:
918 """Add the parameters to the host's namespace."""
919 for item in self._parameterItems:
920 for normalizedIdentifier in normalizedIdentifiersOf(item):
921 self._namespace.AddElement(normalizedIdentifier, item)
924@export
925class DeclarationRegionMixin(
926 metaclass=ExtendedType,
927 mixin=True,
928 expects=(
929 "_namespace",
930 "_functions",
931 "_procedures",
932 "_declaredItems",
933 "_Declarations",
934 "_IndexDeclaredItem",
935 "_UnindexDeclaredItem",
936 "IndexDeclaredItems",
937 "_Labels"
938 )
939):
940 """
941 A base-class for the concurrent, sequential and protected type declaration region mixins.
943 It carries what all regions share: adding interface items to the region's namespace, the mutators for declared items
944 (e.g. :meth:`AddDeclaredItem`), and the hooks for declared items no region handles itself.
946 The derived region mixin-class provides the namespace ``_namespace``, the declared items ``_declaredItems`` and the
947 lookup tables ``_functions`` and ``_procedures``; it or the host class provides the methods ``_Declarations()``,
948 ``_IndexDeclaredItem(item)``, ``_UnindexDeclaredItem(item)`` and ``IndexDeclaredItems()``.
950 An interface item shares the declarative region of the declarative part beside it - VHDL rejects
951 ``port (g : in bit)`` beside ``generic (g : integer)``, ``signal x`` beside ``port (x : in bit)``, and a
952 subprogram variable named like one of its parameters, all as "identifier already used for a
953 declaration". So they belong in the region's *own* namespace, not a separate one.
955 Which of the three a region has is known statically by the class that declares them, so each derived
956 class calls the ones it needs from its own :meth:`IndexDeclaredItems` before delegating upwards.
957 Interface items are added to the namespace only - ``GenericItems``/``PortItems``/``ParameterItems``
958 already expose them as ordered lists, so no extra lookup table is needed.
960 .. seealso::
962 * :class:`Concurrent declaration region mixin <pyVHDLModel.Regions.ConcurrentDeclarationRegionMixin>`
963 * :class:`Sequential declaration region mixin <pyVHDLModel.Regions.SequentialDeclarationRegionMixin>`
964 * :class:`Protected type declaration region mixin <pyVHDLModel.Regions.ProtectedTypeDeclarationRegionMixin>`
966 A statement label is declared implicitly in the declarative region enclosing the statement, so the host provides the
967 labeled statements of the region by ``_Labels()``: a host with statements gets it from its statements mixin
968 (e.g. :class:`~pyVHDLModel.Concurrent.ConcurrentStatementsMixin`), a host without statements from
969 :class:`WithoutStatementsMixin`.
970 """
971 _DECLARED_ITEM_TYPES: ClassVar[tuple[type, ...]] #: Accepted classes of declared items (:mod:`pyVHDLModel`).
972 _areDeclaredItemsIndexed: bool #: ``True`` if :meth:`IndexDeclaredItems` filled the namespace.
974 def __init__(self) -> None:
975 """
976 Initializes a declaration region, which isn't indexed yet.
977 """
978 self._areDeclaredItemsIndexed = False
980 def _AddToNamespace(self, element: NamedEntityMixin | MultipleNamedEntityMixin) -> None:
981 """
982 Add an element's identifiers to this region's namespace.
984 Nothing is added to a region not indexed yet: :meth:`IndexDeclaredItems` adds the element later. A generic
985 subprogram is an overload (:meth:`_AddOverloadToNamespace`).
987 :param element: The named element to add, e.g. an interface item.
988 """
989 from pyVHDLModel.Subprogram import Function, Procedure
991 if not self._areDeclaredItemsIndexed:
992 return
993 elif isinstance(element, (Function, Procedure)):
994 self._AddOverloadToNamespace(element)
995 return
997 for normalizedIdentifier in normalizedIdentifiersOf(element):
998 self._namespace.AddElement(normalizedIdentifier, element)
1000 def _RemoveFromNamespace(self, element: NamedEntityMixin | MultipleNamedEntityMixin) -> None:
1001 """
1002 Remove an element's identifiers from this region's namespace.
1004 Nothing is removed from a region not indexed yet, as its namespace doesn't contain the element. A generic
1005 subprogram is an overload (:meth:`_RemoveOverloadFromNamespace`).
1007 :param element: The named element to remove, e.g. an interface item.
1008 :raises NotInNamespaceError: If the region is indexed, and an identifier of the element isn't declared in the
1009 namespace.
1010 :raises NotInNamespaceError: If the region is indexed, and an identifier of the element refers to another element,
1011 e.g. a duplicate declaration.
1012 """
1013 from pyVHDLModel.Subprogram import Function, Procedure
1015 if not self._areDeclaredItemsIndexed:
1016 return
1017 elif isinstance(element, (Function, Procedure)):
1018 self._RemoveOverloadFromNamespace(element)
1019 return
1021 for normalizedIdentifier in normalizedIdentifiersOf(element):
1022 self._namespace.RemoveElement(normalizedIdentifier, element)
1024 def _IndexOtherDeclaredItem(self, item: ModelEntity) -> None:
1025 """
1026 Hook for declared items the region doesn't handle itself. Derived classes may override it.
1028 :param item: The declared item.
1029 """
1031 def _UnindexOtherDeclaredItem(self, item: ModelEntity) -> None:
1032 """
1033 Hook to remove declared items the region doesn't handle itself. Derived classes may override it.
1035 :param item: The declared item.
1036 """
1038 def _RemoveDeclaredItemsFromNamespace(self) -> None:
1039 """
1040 Remove all declared items of this region from its namespace.
1042 Interface items of this region and items imported from elsewhere (e.g. by a use clause) are kept.
1043 """
1044 from pyVHDLModel.Interface import InterfaceItemMixin
1046 for normalizedIdentifier, element in list(self._namespace.Elements().items()):
1047 if not isinstance(element, InterfaceItemMixin) and element._parent is self:
1048 self._namespace.RemoveElement(normalizedIdentifier, element)
1050 def _RemoveDeclaredItemEntries(
1051 self,
1052 lookupTable: dict[str, Any],
1053 item: NamedEntityMixin | MultipleNamedEntityMixin
1054 ) -> None:
1055 """
1056 Remove a declared item's entries from a lookup table and from the namespace.
1058 Every identifier is checked before anything is removed.
1060 :param lookupTable: The lookup table matching the declared item's kind.
1061 :param item: The declared item.
1062 :raises NotInNamespaceError: If an identifier of the declared item isn't declared in the namespace.
1063 :raises NotInNamespaceError: If an identifier of the declared item refers to another element, e.g. a duplicate
1064 declaration.
1065 """
1066 normalizedIdentifiers = normalizedIdentifiersOf(item)
1067 elements = self._namespace.Elements()
1068 for normalizedIdentifier in normalizedIdentifiers:
1069 if elements.get(normalizedIdentifier) is not item:
1070 # raises NotInNamespaceError, naming the reason
1071 self._namespace.RemoveElement(normalizedIdentifier, item)
1073 for normalizedIdentifier in normalizedIdentifiers:
1074 del lookupTable[normalizedIdentifier]
1075 self._namespace.RemoveElement(normalizedIdentifier, item)
1077 def _IndexOverload(self, item: NamedEntityMixin | MultipleNamedEntityMixin) -> None:
1078 """
1079 Add an inserted function or procedure to its lookup table and to the namespace.
1081 The list of overloads follows the declaration order. The namespace is updated by :meth:`_AddOverloadToNamespace`.
1083 :param item: The inserted function or procedure.
1084 """
1085 from pyVHDLModel.Subprogram import Function, Procedure
1087 kind = Function if isinstance(item, Function) else Procedure
1088 lookupTable = self._functions if kind is Function else self._procedures
1089 normalizedIdentifier = item._normalizedIdentifier
1090 lookupTable[normalizedIdentifier] = [
1091 declaration for declaration in self._declaredItems
1092 if isinstance(declaration, kind) and declaration._normalizedIdentifier == normalizedIdentifier
1093 ]
1095 self._AddOverloadToNamespace(item)
1097 def _UnindexOverload(self, item: NamedEntityMixin | MultipleNamedEntityMixin) -> None:
1098 """
1099 Remove a function or procedure from its lookup table and from the namespace.
1101 The namespace is updated first, by :meth:`_RemoveOverloadFromNamespace`.
1103 :param item: The function or procedure to remove, still a declared item of this region.
1104 :raises NotInNamespaceError: If the identifier isn't declared in the namespace.
1105 :raises NotInNamespaceError: If the identifier refers to an element, which is no overload, e.g. a duplicate
1106 declaration.
1107 """
1108 from pyVHDLModel.Subprogram import Function
1110 self._RemoveOverloadFromNamespace(item)
1112 normalizedIdentifier = item._normalizedIdentifier
1113 lookupTable = self._functions if isinstance(item, Function) else self._procedures
1114 overloads = lookupTable[normalizedIdentifier]
1115 overloads.remove(item)
1116 if len(overloads) == 0:
1117 del lookupTable[normalizedIdentifier]
1119 def _AddOverloadToNamespace(self, item: NamedEntityMixin | MultipleNamedEntityMixin) -> None:
1120 """
1121 Add a function or procedure to the namespace, if it's the overload declared last.
1123 The namespace keeps one entry per identifier: the overload declared last, as after :meth:`IndexDeclaredItems`, which
1124 adds interface items first, then declared items (:meth:`_Declarations`).
1126 :param item: The function or procedure, already an interface item or declared item of this region.
1127 """
1128 from pyVHDLModel.Subprogram import Function, Procedure
1130 normalizedIdentifier = item._normalizedIdentifier
1131 overloads = [
1132 declaration for declaration in self._Declarations()
1133 if isinstance(declaration, (Function, Procedure)) and declaration._normalizedIdentifier == normalizedIdentifier
1134 ]
1135 if overloads[-1] is item:
1136 self._namespace.AddElement(normalizedIdentifier, item, overloadable=True)
1138 def _RemoveOverloadFromNamespace(self, item: NamedEntityMixin | MultipleNamedEntityMixin) -> None:
1139 """
1140 Remove a function or procedure from the namespace.
1142 Removing the overload the namespace refers to, the namespace refers to the remaining overload declared last
1143 (:meth:`_Declarations`), or the identifier is removed, if no overload remains. Removing another overload leaves the
1144 namespace unchanged. The namespace is checked before it's changed.
1146 :param item: The function or procedure, still an interface item or declared item of this region.
1147 :raises NotInNamespaceError: If the identifier isn't declared in the namespace.
1148 :raises NotInNamespaceError: If the identifier refers to an element, which is no overload, e.g. a duplicate
1149 declaration.
1150 """
1151 from pyVHDLModel.Subprogram import Function, Procedure
1153 normalizedIdentifier = item._normalizedIdentifier
1154 entry = self._namespace.Elements().get(normalizedIdentifier)
1155 if entry is item:
1156 remaining = [
1157 declaration for declaration in self._Declarations()
1158 if declaration is not item
1159 and isinstance(declaration, (Function, Procedure))
1160 and declaration._normalizedIdentifier == normalizedIdentifier
1161 ]
1162 if len(remaining) == 0:
1163 self._namespace.RemoveElement(normalizedIdentifier, item)
1164 else:
1165 self._namespace.AddElement(normalizedIdentifier, remaining[-1], overloadable=True)
1166 elif not isinstance(entry, (Function, Procedure)):
1167 # raises NotInNamespaceError, naming the reason
1168 self._namespace.RemoveElement(normalizedIdentifier, item)
1170 def _SharedRegions(self) -> tuple[DeclarationRegionMixin, ...]:
1171 """
1172 Return the other language constructs forming one declarative region with this one.
1174 An entity and each of its architectures form one declarative region, as do a package and its body. Derived classes
1175 override it.
1177 :returns: Tuple of declarative regions.
1178 """
1179 return ()
1181 def _MayShareIdentifier(self, item: ModelEntity, existing: ModelEntity) -> bool:
1182 """
1183 Check if two declarations may share an identifier in one declarative region.
1185 Subprograms are overloadable. A protected type body completes its protected type, a full type completes its
1186 incomplete type, and a constant completes its deferred constant.
1188 :param item: The new declaration.
1189 :param existing: The existing declaration.
1190 :returns: ``True``, if both declarations may share an identifier.
1191 """
1192 from pyVHDLModel.Interface import InterfaceItemMixin
1193 from pyVHDLModel.Subprogram import Function, Procedure
1194 from pyVHDLModel.Type import FullType, AnonymousType, ProtectedType, ProtectedTypeBody
1196 return (
1197 (isinstance(item, (Function, Procedure)) and isinstance(existing, (Function, Procedure)))
1198 or (isinstance(item, ProtectedTypeBody) and isinstance(existing, ProtectedType))
1199 or (isinstance(item, ProtectedType) and isinstance(existing, ProtectedTypeBody))
1200 or (isinstance(item, FullType) and isinstance(existing, AnonymousType))
1201 or (isinstance(item, AnonymousType) and isinstance(existing, FullType))
1202 or (
1203 isinstance(item, Constant) and not isinstance(item, InterfaceItemMixin)
1204 and isinstance(existing, DeferredConstant)
1205 )
1206 or (
1207 isinstance(item, DeferredConstant)
1208 and isinstance(existing, Constant) and not isinstance(existing, InterfaceItemMixin)
1209 )
1210 )
1212 def _ClashingDeclarations(self, item: ModelEntity) -> tuple[ModelEntity, ...]:
1213 """
1214 Return the declarations sharing an identifier with the given item, which may not share it.
1216 The declarations (:meth:`_Declarations`) and the labeled statements (``_Labels()``) of this declarative region and
1217 of the language constructs sharing it (:meth:`_SharedRegions`) are searched: a statement label is declared
1218 implicitly. Items imported into the namespace (e.g. by a use clause) are no declarations.
1220 :param item: The new declaration, or the new labeled statement.
1221 :returns: Tuple of clashing declarations, interface items first, then declared items in declaration order, then
1222 labeled statements.
1223 """
1224 if len(names := set(normalizedDeclaredNamesOf(item))) == 0:
1225 return ()
1227 return tuple(
1228 declaration
1229 for region in (self, *self._SharedRegions())
1230 for declaration in (*region._Declarations(), *region._Labels())
1231 if (
1232 declaration is not item
1233 and not names.isdisjoint(normalizedDeclaredNamesOf(declaration))
1234 and not self._MayShareIdentifier(item, declaration)
1235 )
1236 )
1238 def _DeclaredItemsSharingIdentifiers(self, declaredItem: ModelEntity) -> list[ModelEntity]:
1239 """
1240 Return the other declared items of this region sharing an identifier with the given declared item.
1242 :param declaredItem: The declared item.
1243 :returns: List of declared items, in declaration order.
1244 """
1245 if not isinstance(declaredItem, (NamedEntityMixin, MultipleNamedEntityMixin)):
1246 return []
1248 identifiers = set(normalizedIdentifiersOf(declaredItem))
1249 return [
1250 item for item in self._declaredItems
1251 if item is not declaredItem
1252 and isinstance(item, (NamedEntityMixin, MultipleNamedEntityMixin))
1253 and not identifiers.isdisjoint(normalizedIdentifiersOf(item))
1254 ]
1256 def AddDeclaredItem(self, declaredItem: ModelEntity) -> None:
1257 """
1258 Add a declared item at the end of the declarative region.
1260 The declared item's parent is set to this declarative region. If the region is indexed, its identifiers are added
1261 to the region's namespace and to the lookup table matching its kind.
1263 :param declaredItem: The declared item to add.
1264 :raises ValueError: If parameter 'declaredItem' is ``None``.
1265 :raises TypeError: If parameter 'declaredItem' is not a declaration allowed in this declarative
1266 region.
1267 :raises AlreadyInDesignError: If parameter 'declaredItem' already has a parent.
1268 :raises DuplicateDeclarationError: If an existing declaration of the declarative region shares an
1269 identifier with the declared item.
1271 .. seealso::
1273 :meth:`InsertDeclaredItem`
1274 |rarr| Insert a declared item at a position of the declarative region.
1275 :meth:`RemoveDeclaredItem`
1276 |rarr| Remove a declared item from the declarative region.
1277 """
1278 self.InsertDeclaredItem(len(self._declaredItems), declaredItem)
1280 def InsertDeclaredItem(self, index: int, declaredItem: ModelEntity) -> None:
1281 """
1282 Insert a declared item at a position of the declarative region.
1284 The declared item's parent is set to this declarative region. If the region is indexed, its identifiers are added
1285 to the region's namespace and to the lookup table matching its kind.
1287 An identifier is declared once per declarative region: interface items and declared items count, including those of
1288 the language construct sharing the declarative region (an entity and its architecture, a package and its body).
1289 Subprograms are overloadable: a subprogram may share its identifier with other subprograms. A protected type body
1290 may share its identifier with its protected type, a full type with its incomplete type declaration, and a constant
1291 with its deferred constant. Items imported into the namespace (e.g. by a use clause) are hidden.
1293 :param index: Position, from ``0`` (first) to the number of declared items (append).
1294 A negative index counts from the end, as for :meth:`list.insert`: ``-1``
1295 inserts before the last declared item.
1296 :param declaredItem: The declared item to insert.
1297 :raises ValueError: If parameter 'index' is ``None``.
1298 :raises TypeError: If parameter 'index' is not of type :class:`int`.
1299 :raises ValueError: If parameter 'index' is out of range.
1300 :raises ValueError: If parameter 'declaredItem' is ``None``.
1301 :raises TypeError: If parameter 'declaredItem' is not a declaration allowed in this declarative
1302 region.
1303 :raises AlreadyInDesignError: If parameter 'declaredItem' already has a parent.
1304 :raises DuplicateDeclarationError: If an existing declaration of the declarative region shares an
1305 identifier with the declared item.
1307 .. seealso::
1309 :meth:`AddDeclaredItem`
1310 |rarr| Add a declared item at the end of the declarative region.
1311 :meth:`InsertDeclaredItemBefore`
1312 |rarr| Insert a declared item before another declared item.
1313 :meth:`InsertDeclaredItemAfter`
1314 |rarr| Insert a declared item after another declared item.
1315 :meth:`RemoveDeclaredItem`
1316 |rarr| Remove a declared item from the declarative region.
1317 :meth:`MoveDeclaredItem`
1318 |rarr| Move a declared item to a position of the declarative region.
1319 """
1320 from pyVHDLModel.DesignUnit import Component
1321 from pyVHDLModel.Interface import InterfaceItemMixin
1322 from pyVHDLModel.Subprogram import Function, Procedure
1323 from pyVHDLModel.Type import BaseType, Subtype
1325 if index is None:
1326 raise ValueError("Parameter 'index' is None.")
1327 elif not isinstance(index, int):
1328 ex = TypeError("Parameter 'index' is not of type 'int'.")
1329 ex.add_note(f"Got type '{getFullyQualifiedName(index)}'.")
1330 raise ex
1331 elif not (-len(self._declaredItems) <= index <= len(self._declaredItems)):
1332 ex = ValueError(f"Parameter 'index' is out of range {-len(self._declaredItems)}..{len(self._declaredItems)}.")
1333 ex.add_note(f"Got value '{index}'.")
1334 raise ex
1336 if declaredItem is None:
1337 raise ValueError("Parameter 'declaredItem' is None.")
1338 elif not isinstance(declaredItem, self._DECLARED_ITEM_TYPES) or isinstance(declaredItem, InterfaceItemMixin):
1339 ex = TypeError(f"Parameter 'declaredItem' is not a declaration allowed in {getTypeName(self)}.")
1340 ex.add_note(f"Got type '{getFullyQualifiedName(declaredItem)}'.")
1341 raise ex
1342 elif declaredItem._parent is not None:
1343 identifiers = ""
1344 if isinstance(declaredItem, (NamedEntityMixin, MultipleNamedEntityMixin)):
1345 identifiers = f" '{', '.join(identifiersOf(declaredItem))}'"
1346 ex = AlreadyInDesignError(
1347 f"{getTypeName(declaredItem)}{identifiers} already has a parent.",
1348 element=declaredItem,
1349 parent=declaredItem._parent
1350 )
1351 ex.add_note(f"Remove it from its {getTypeName(declaredItem._parent)} first.")
1352 raise ex
1354 if len(clashes := self._ClashingDeclarations(declaredItem)) > 0:
1355 existingIdentifiers = normalizedDeclaredNamesOf(clashes[0])
1356 newIdentifiers = zip(identifiersOf(declaredItem), normalizedIdentifiersOf(declaredItem))
1357 identifier = next(name for name, normalizedName in newIdentifiers if normalizedName in existingIdentifiers)
1359 if isinstance(declaredItem, Subtype):
1360 errorClass = DuplicateSubtypeError
1361 elif isinstance(declaredItem, BaseType):
1362 errorClass = DuplicateTypeError
1363 elif isinstance(declaredItem, (Function, Procedure)):
1364 errorClass = DuplicateSubprogramError
1365 elif isinstance(declaredItem, BaseConstant):
1366 errorClass = DuplicateConstantError
1367 elif isinstance(declaredItem, Signal):
1368 errorClass = DuplicateSignalError
1369 elif isinstance(declaredItem, (Variable, SharedVariable)):
1370 errorClass = DuplicateVariableError
1371 elif isinstance(declaredItem, File):
1372 errorClass = DuplicateFileError
1373 elif isinstance(declaredItem, Component):
1374 errorClass = DuplicateComponentError
1375 else:
1376 errorClass = DuplicateDeclarationError
1377 region = clashes[0]._parent
1378 while not isinstance(region, DeclarationRegionMixin):
1379 region = region._parent
1380 ex = errorClass(
1381 f"{getTypeName(region)} already declares '{identifier}'.",
1382 identifier=identifier,
1383 namespace=self._namespace,
1384 element=declaredItem,
1385 existing=clashes[0]
1386 )
1387 ex.add_note(f"Existing declaration is of type '{getFullyQualifiedName(clashes[0])}'.")
1388 raise ex
1390 sharingItems = self._DeclaredItemsSharingIdentifiers(declaredItem)
1392 self._declaredItems.insert(index, declaredItem)
1393 declaredItem.Parent = self
1394 if self._areDeclaredItemsIndexed:
1395 if isinstance(declaredItem, (Function, Procedure)):
1396 self._IndexOverload(declaredItem)
1397 else:
1398 self._IndexDeclaredItem(declaredItem)
1400 def InsertDeclaredItemBefore(self, declaredItem: ModelEntity, before: ModelEntity) -> None:
1401 """
1402 Insert a declared item before another declared item.
1404 The declared item's parent is set to this declarative region. If the region is indexed, its identifiers are added
1405 to the region's namespace and to the lookup table matching its kind.
1407 :param declaredItem: The declared item to insert.
1408 :param before: The declared item of this declarative region to insert before.
1409 :raises ValueError: If parameter 'before' is ``None``.
1410 :raises TypeError: If parameter 'before' is not of type :class:`~pyVHDLModel.Base.ModelEntity`.
1411 :raises NotAChildError: If parameter 'before' is not a declared item of this declarative region.
1412 :raises ValueError: If parameter 'declaredItem' is ``None``.
1413 :raises TypeError: If parameter 'declaredItem' is not a declaration allowed in this declarative
1414 region.
1415 :raises AlreadyInDesignError: If parameter 'declaredItem' already has a parent.
1416 :raises DuplicateDeclarationError: If an existing declaration of the declarative region shares an
1417 identifier with the declared item.
1419 .. seealso::
1421 :meth:`InsertDeclaredItem`
1422 |rarr| Insert a declared item at a position of the declarative region.
1423 :meth:`InsertDeclaredItemAfter`
1424 |rarr| Insert a declared item after another declared item.
1425 """
1426 if before is None:
1427 raise ValueError("Parameter 'before' is None.")
1428 elif not isinstance(before, ModelEntity):
1429 ex = TypeError("Parameter 'before' is not of type 'ModelEntity'.")
1430 ex.add_note(f"Got type '{getFullyQualifiedName(before)}'.")
1431 raise ex
1433 for index, item in enumerate(self._declaredItems):
1434 if item is before:
1435 break
1436 else:
1437 identifiers = ""
1438 if isinstance(before, (NamedEntityMixin, MultipleNamedEntityMixin)): 1438 ↛ 1440line 1438 didn't jump to line 1440 because the condition on line 1438 was always true
1439 identifiers = f" '{', '.join(identifiersOf(before))}'"
1440 raise NotAChildError(
1441 f"{getTypeName(before)}{identifiers} is not a declared item of this {getTypeName(self)}.",
1442 element=before,
1443 parent=self
1444 )
1446 self.InsertDeclaredItem(index, declaredItem)
1448 def InsertDeclaredItemAfter(self, declaredItem: ModelEntity, after: ModelEntity) -> None:
1449 """
1450 Insert a declared item after another declared item.
1452 The declared item's parent is set to this declarative region. If the region is indexed, its identifiers are added
1453 to the region's namespace and to the lookup table matching its kind.
1455 :param declaredItem: The declared item to insert.
1456 :param after: The declared item of this declarative region to insert after.
1457 :raises ValueError: If parameter 'after' is ``None``.
1458 :raises TypeError: If parameter 'after' is not of type :class:`~pyVHDLModel.Base.ModelEntity`.
1459 :raises NotAChildError: If parameter 'after' is not a declared item of this declarative region.
1460 :raises ValueError: If parameter 'declaredItem' is ``None``.
1461 :raises TypeError: If parameter 'declaredItem' is not a declaration allowed in this declarative
1462 region.
1463 :raises AlreadyInDesignError: If parameter 'declaredItem' already has a parent.
1464 :raises DuplicateDeclarationError: If an existing declaration of the declarative region shares an
1465 identifier with the declared item.
1467 .. seealso::
1469 :meth:`InsertDeclaredItem`
1470 |rarr| Insert a declared item at a position of the declarative region.
1471 :meth:`InsertDeclaredItemBefore`
1472 |rarr| Insert a declared item before another declared item.
1473 """
1474 if after is None:
1475 raise ValueError("Parameter 'after' is None.")
1476 elif not isinstance(after, ModelEntity):
1477 ex = TypeError("Parameter 'after' is not of type 'ModelEntity'.")
1478 ex.add_note(f"Got type '{getFullyQualifiedName(after)}'.")
1479 raise ex
1481 for index, item in enumerate(self._declaredItems):
1482 if item is after:
1483 break
1484 else:
1485 identifiers = ""
1486 if isinstance(after, (NamedEntityMixin, MultipleNamedEntityMixin)): 1486 ↛ 1487line 1486 didn't jump to line 1487 because the condition on line 1486 was never true
1487 identifiers = f" '{', '.join(identifiersOf(after))}'"
1488 raise NotAChildError(
1489 f"{getTypeName(after)}{identifiers} is not a declared item of this {getTypeName(self)}.",
1490 element=after,
1491 parent=self
1492 )
1494 self.InsertDeclaredItem(index + 1, declaredItem)
1496 def RemoveDeclaredItem(self, declaredItem: ModelEntity) -> None:
1497 """
1498 Remove a declared item from the declarative region.
1500 The declared item is detached: its parent is cleared. If the region is indexed, the declared item's identifiers are
1501 removed from the region's namespace and from the lookup table matching its kind first; they are checked before
1502 anything is changed. The declared item itself is unchanged and can be added to another declarative region, or
1503 inserted at another position.
1505 The namespace refers to the overload declared last: removing it, the namespace refers to the remaining overload
1506 declared last. If another declaration shares an identifier with the declared item (e.g. a duplicate declaration
1507 read by a parser), the region is indexed again (:meth:`IndexDeclaredItems`) after the removal.
1509 :param declaredItem: The declared item to remove.
1510 :raises ValueError: If parameter 'declaredItem' is ``None``.
1511 :raises TypeError: If parameter 'declaredItem' is not of type :class:`~pyVHDLModel.Base.ModelEntity`.
1512 :raises NotAChildError: If parameter 'declaredItem' is not a declared item of this declarative region.
1513 :raises NotInNamespaceError: If the region is indexed, and an identifier of the declared item isn't declared in
1514 its namespace.
1515 :raises NotInNamespaceError: If the region is indexed, and an identifier of the declared item refers to another
1516 declaration, e.g. a duplicate declaration.
1518 .. seealso::
1520 :meth:`AddDeclaredItem`
1521 |rarr| Add a declared item at the end of the declarative region.
1522 :meth:`InsertDeclaredItem`
1523 |rarr| Insert a declared item at a position of the declarative region.
1524 :meth:`MoveDeclaredItem`
1525 |rarr| Move a declared item to a position of the declarative region.
1526 """
1527 from pyVHDLModel.Subprogram import Function, Procedure
1529 if declaredItem is None:
1530 raise ValueError("Parameter 'declaredItem' is None.")
1531 elif not isinstance(declaredItem, ModelEntity):
1532 ex = TypeError("Parameter 'declaredItem' is not of type 'ModelEntity'.")
1533 ex.add_note(f"Got type '{getFullyQualifiedName(declaredItem)}'.")
1534 raise ex
1536 for index, item in enumerate(self._declaredItems):
1537 if item is declaredItem:
1538 break
1539 else:
1540 identifiers = ""
1541 if isinstance(declaredItem, (NamedEntityMixin, MultipleNamedEntityMixin)): 1541 ↛ 1543line 1541 didn't jump to line 1543 because the condition on line 1541 was always true
1542 identifiers = f" '{', '.join(identifiersOf(declaredItem))}'"
1543 raise NotAChildError(
1544 f"{getTypeName(declaredItem)}{identifiers} is not a declared item of this {getTypeName(self)}.",
1545 element=declaredItem,
1546 parent=self
1547 )
1549 reindex = False
1550 if self._areDeclaredItemsIndexed:
1551 isOverload = isinstance(declaredItem, (Function, Procedure))
1552 reindex = any(
1553 not (isOverload and isinstance(item, (Function, Procedure)))
1554 for item in self._DeclaredItemsSharingIdentifiers(declaredItem)
1555 )
1556 self._UnindexDeclaredItem(declaredItem)
1558 del self._declaredItems[index]
1559 declaredItem._parent = None
1561 if reindex:
1562 self.IndexDeclaredItems()
1564 def MoveDeclaredItem(self, declaredItem: ModelEntity, index: int) -> None:
1565 """
1566 Move a declared item to a position of the declarative region.
1568 The declared item stays a declared item of this declarative region: only the order of the declared items changes.
1570 If the region is indexed, the lookup tables and the namespace follow the new order: a list of overloads (e.g. in
1571 ``Functions``) follows the declaration order, and the namespace refers to the overload declared last. If
1572 another declaration shares an identifier with the declared item (e.g. a duplicate declaration read by a parser),
1573 the region is indexed again (:meth:`IndexDeclaredItems`) after the move.
1575 :param declaredItem: The declared item to move.
1576 :param index: Position of the declared item after the move, from ``0`` (first) to the number of
1577 declared items minus one (last). A negative index counts from the end, as for a
1578 :class:`list` index: ``-1`` moves the declared item to the end.
1579 :raises ValueError: If parameter 'declaredItem' is ``None``.
1580 :raises TypeError: If parameter 'declaredItem' is not of type :class:`~pyVHDLModel.Base.ModelEntity`.
1581 :raises NotAChildError: If parameter 'declaredItem' is not a declared item of this declarative region.
1582 :raises ValueError: If parameter 'index' is ``None``.
1583 :raises TypeError: If parameter 'index' is not of type :class:`int`.
1584 :raises ValueError: If parameter 'index' is out of range.
1586 .. seealso::
1588 :meth:`MoveDeclaredItemBefore`
1589 |rarr| Move a declared item before another declared item.
1590 :meth:`MoveDeclaredItemAfter`
1591 |rarr| Move a declared item after another declared item.
1592 :meth:`InsertDeclaredItem`
1593 |rarr| Insert a declared item at a position of the declarative region.
1594 :meth:`RemoveDeclaredItem`
1595 |rarr| Remove a declared item from the declarative region.
1596 """
1597 from pyVHDLModel.Subprogram import Function, Procedure
1599 if declaredItem is None:
1600 raise ValueError("Parameter 'declaredItem' is None.")
1601 elif not isinstance(declaredItem, ModelEntity):
1602 ex = TypeError("Parameter 'declaredItem' is not of type 'ModelEntity'.")
1603 ex.add_note(f"Got type '{getFullyQualifiedName(declaredItem)}'.")
1604 raise ex
1606 for currentIndex, item in enumerate(self._declaredItems):
1607 if item is declaredItem:
1608 break
1609 else:
1610 identifiers = ""
1611 if isinstance(declaredItem, (NamedEntityMixin, MultipleNamedEntityMixin)): 1611 ↛ 1613line 1611 didn't jump to line 1613 because the condition on line 1611 was always true
1612 identifiers = f" '{', '.join(identifiersOf(declaredItem))}'"
1613 raise NotAChildError(
1614 f"{getTypeName(declaredItem)}{identifiers} is not a declared item of this {getTypeName(self)}.",
1615 element=declaredItem,
1616 parent=self
1617 )
1619 if index is None:
1620 raise ValueError("Parameter 'index' is None.")
1621 elif not isinstance(index, int):
1622 ex = TypeError("Parameter 'index' is not of type 'int'.")
1623 ex.add_note(f"Got type '{getFullyQualifiedName(index)}'.")
1624 raise ex
1625 elif not (-len(self._declaredItems) <= index < len(self._declaredItems)):
1626 ex = ValueError(f"Parameter 'index' is out of range {-len(self._declaredItems)}..{len(self._declaredItems) - 1}.")
1627 ex.add_note(f"Got value '{index}'.")
1628 raise ex
1630 newIndex = index if index >= 0 else len(self._declaredItems) + index
1631 if newIndex == currentIndex:
1632 return
1634 reindex = False
1635 overloads = False
1636 if self._areDeclaredItemsIndexed and len(sharingItems := self._DeclaredItemsSharingIdentifiers(declaredItem)) > 0:
1637 isOverload = isinstance(declaredItem, (Function, Procedure))
1638 reindex = any(not (isOverload and isinstance(item, (Function, Procedure))) for item in sharingItems)
1639 overloads = not reindex
1641 if overloads:
1642 self._RemoveOverloadFromNamespace(declaredItem)
1644 del self._declaredItems[currentIndex]
1645 self._declaredItems.insert(newIndex, declaredItem)
1647 if reindex:
1648 self.IndexDeclaredItems()
1649 elif overloads:
1650 self._IndexOverload(declaredItem)
1652 def MoveDeclaredItemBefore(self, declaredItem: ModelEntity, before: ModelEntity) -> None:
1653 """
1654 Move a declared item before another declared item.
1656 The declared item stays a declared item of this declarative region: only the order of the declared items changes,
1657 as for :meth:`MoveDeclaredItem`. Moving a declared item before itself keeps it in place.
1659 :param declaredItem: The declared item to move.
1660 :param before: The declared item of this declarative region to move before.
1661 :raises ValueError: If parameter 'before' is ``None``.
1662 :raises TypeError: If parameter 'before' is not of type :class:`~pyVHDLModel.Base.ModelEntity`.
1663 :raises NotAChildError: If parameter 'before' is not a declared item of this declarative region.
1664 :raises ValueError: If parameter 'declaredItem' is ``None``.
1665 :raises TypeError: If parameter 'declaredItem' is not of type :class:`~pyVHDLModel.Base.ModelEntity`.
1666 :raises NotAChildError: If parameter 'declaredItem' is not a declared item of this declarative region.
1668 .. seealso::
1670 :meth:`MoveDeclaredItem`
1671 |rarr| Move a declared item to a position of the declarative region.
1672 :meth:`MoveDeclaredItemAfter`
1673 |rarr| Move a declared item after another declared item.
1674 """
1675 if before is None:
1676 raise ValueError("Parameter 'before' is None.")
1677 elif not isinstance(before, ModelEntity):
1678 ex = TypeError("Parameter 'before' is not of type 'ModelEntity'.")
1679 ex.add_note(f"Got type '{getFullyQualifiedName(before)}'.")
1680 raise ex
1682 for index, item in enumerate(self._declaredItems):
1683 if item is before:
1684 break
1685 else:
1686 identifiers = ""
1687 if isinstance(before, (NamedEntityMixin, MultipleNamedEntityMixin)): 1687 ↛ 1689line 1687 didn't jump to line 1689 because the condition on line 1687 was always true
1688 identifiers = f" '{', '.join(identifiersOf(before))}'"
1689 raise NotAChildError(
1690 f"{getTypeName(before)}{identifiers} is not a declared item of this {getTypeName(self)}.",
1691 element=before,
1692 parent=self
1693 )
1695 precedes = any(item is declaredItem for item in self._declaredItems[:index])
1696 self.MoveDeclaredItem(declaredItem, index - 1 if precedes else index)
1698 def MoveDeclaredItemAfter(self, declaredItem: ModelEntity, after: ModelEntity) -> None:
1699 """
1700 Move a declared item after another declared item.
1702 The declared item stays a declared item of this declarative region: only the order of the declared items changes,
1703 as for :meth:`MoveDeclaredItem`. Moving a declared item after itself keeps it in place.
1705 :param declaredItem: The declared item to move.
1706 :param after: The declared item of this declarative region to move after.
1707 :raises ValueError: If parameter 'after' is ``None``.
1708 :raises TypeError: If parameter 'after' is not of type :class:`~pyVHDLModel.Base.ModelEntity`.
1709 :raises NotAChildError: If parameter 'after' is not a declared item of this declarative region.
1710 :raises ValueError: If parameter 'declaredItem' is ``None``.
1711 :raises TypeError: If parameter 'declaredItem' is not of type :class:`~pyVHDLModel.Base.ModelEntity`.
1712 :raises NotAChildError: If parameter 'declaredItem' is not a declared item of this declarative region.
1714 .. seealso::
1716 :meth:`MoveDeclaredItem`
1717 |rarr| Move a declared item to a position of the declarative region.
1718 :meth:`MoveDeclaredItemBefore`
1719 |rarr| Move a declared item before another declared item.
1720 """
1721 if after is None:
1722 raise ValueError("Parameter 'after' is None.")
1723 elif not isinstance(after, ModelEntity): 1723 ↛ 1724line 1723 didn't jump to line 1724 because the condition on line 1723 was never true
1724 ex = TypeError("Parameter 'after' is not of type 'ModelEntity'.")
1725 ex.add_note(f"Got type '{getFullyQualifiedName(after)}'.")
1726 raise ex
1728 for index, item in enumerate(self._declaredItems):
1729 if item is after:
1730 break
1731 else:
1732 identifiers = ""
1733 if isinstance(after, (NamedEntityMixin, MultipleNamedEntityMixin)): 1733 ↛ 1735line 1733 didn't jump to line 1735 because the condition on line 1733 was always true
1734 identifiers = f" '{', '.join(identifiersOf(after))}'"
1735 raise NotAChildError(
1736 f"{getTypeName(after)}{identifiers} is not a declared item of this {getTypeName(self)}.",
1737 element=after,
1738 parent=self
1739 )
1741 precedes = any(item is declaredItem for item in self._declaredItems[:index + 1])
1742 self.MoveDeclaredItem(declaredItem, index if precedes else index + 1)
1745@export
1746class WithoutStatementsMixin(metaclass=ExtendedType, mixin=True):
1747 """
1748 A mixin-class for language constructs with a declarative region, but without statements.
1750 It provides the labeled statements ``_Labels()`` the declaration region expects from its host class: none.
1752 .. seealso::
1754 * :class:`Package <pyVHDLModel.DesignUnit.Package>`
1755 * :class:`Package body <pyVHDLModel.DesignUnit.PackageBody>`
1756 * :class:`Protected type <pyVHDLModel.Type.ProtectedType>`
1757 * :class:`Protected type body <pyVHDLModel.Type.ProtectedTypeBody>`
1758 """
1760 def _Labels(self) -> tuple[ModelEntity, ...]:
1761 """
1762 Return the labeled statements, whose labels are declared in this declarative region.
1764 :returns: An empty tuple.
1765 """
1766 return ()
1769@export
1770class ConcurrentDeclarationRegionMixin(DeclarationRegionMixin, mixin=True):
1771 # FIXME: define list prefix type e.g. via Union
1772 """
1773 A mixin-class for concurrent declaration regions.
1775 Entities, architectures, packages, blocks and generate bodies declare items concurrently. Beside
1776 the namespace, the region keeps a lookup table per kind of declared item (:data:`Types`,
1777 :data:`Signals`, :data:`Constants`, ...).
1779 .. seealso::
1781 * :class:`Concurrent block statement <pyVHDLModel.Concurrent.ConcurrentBlockStatement>`
1782 * :class:`Generate branch <pyVHDLModel.Concurrent.GenerateBranch>`
1783 * :class:`Concurrent case <pyVHDLModel.Concurrent.ConcurrentCase>`
1784 * :class:`For generate statement <pyVHDLModel.Concurrent.ForGenerateStatement>`
1785 * :class:`Package <pyVHDLModel.DesignUnit.Package>`
1786 * :class:`Package body <pyVHDLModel.DesignUnit.PackageBody>`
1787 * :class:`Entity <pyVHDLModel.DesignUnit.Entity>`
1788 * :class:`Architecture <pyVHDLModel.DesignUnit.Architecture>`
1789 * :class:`Sequential declaration region <pyVHDLModel.Regions.SequentialDeclarationRegionMixin>`
1790 * :class:`Namespace <pyVHDLModel.Namespace.Namespace>`
1791 """
1792 _declaredItems: list #: List of all declared items in this concurrent declaration region.
1793 _namespace: Namespace #: The namespace of this concurrent declaration region.
1795 # _attributes: dict[str, Attribute]
1796 # _aliases: dict[str, Alias]
1797 _types: dict[str, FullType] #: Dictionary of all types declared in this concurrent declaration region.
1798 _subtypes: dict[str, Subtype] #: Dictionary of all subtypes declared in this concurrent declaration region.
1799 # _objects: dict[str, Union[Constant, Variable, Signal]]
1800 _constants: dict[str, Constant] #: Dictionary of all constants declared in this concurrent declaration region.
1801 _signals: dict[str, Signal] #: Dictionary of all signals declared in this concurrent declaration region.
1802 _sharedVariables: dict[str, SharedVariable] #: Dictionary of all shared variables declared in this concurrent declaration region.
1803 _files: dict[str, File] #: Dictionary of all files declared in this concurrent declaration region.
1804 # _subprograms: dict[str, list[Subprogram]] #: Dictionary of all subprograms declared in this concurrent declaration region.
1805 # FIXME: overloads are only collected into a list, not matched/resolved by signature.
1806 _functions: dict[str, list[Function]] #: Dictionary of all functions declared in this concurrent declaration region, indexed by name; each entry is a list of overloads.
1807 _procedures: dict[str, list[Procedure]] #: Dictionary of all procedures declared in this concurrent declaration region, indexed by name; each entry is a list of overloads.
1808 _components: dict[str, Any] #: Dictionary of all components declared in this concurrent declaration region.
1810 def __init__(
1811 self,
1812 namespaceName: Nullable[str] = None,
1813 declaredItems: Nullable[Iterable] = None,
1814 sharesRegionWithParent: bool = False
1815 ) -> None:
1816 # TODO: extract to mixin
1817 """
1818 Initializes a concurrent declaration region.
1820 :param namespaceName: Name of this region's namespace, usually the host's label or identifier.
1821 :param declaredItems: List of all declared items in this concurrent declaration region.
1822 :param sharesRegionWithParent: Optional, ``True`` if this region continues its parent's declarative region (an
1823 architecture its entity's, a package body its package's). Default: ``False``.
1824 """
1825 DeclarationRegionMixin.__init__(self)
1827 self._namespace = Namespace(namespaceName, sharesRegionWithParent=sharesRegionWithParent)
1829 self._declaredItems = [] # TODO: convert to dict
1830 if declaredItems is not None:
1831 for item in declaredItems:
1832 self._declaredItems.append(item)
1833 item.Parent = self
1835 self._types = {}
1836 self._subtypes = {}
1837 # self._objects = {}
1838 self._constants = {}
1839 self._signals = {}
1840 self._sharedVariables = {}
1841 self._files = {}
1842 # self._subprograms = {}
1843 self._functions = {}
1844 self._procedures = {}
1845 self._components = {}
1847 @readonly
1848 def DeclaredItems(self) -> list:
1849 """
1850 Read-only property to access the declared items (:attr:`_declaredItems`).
1852 :returns: List of declared items.
1853 """
1854 return self._declaredItems
1856 @readonly
1857 def Types(self) -> dict[str, FullType]:
1858 """
1859 Read-only property to access the types (:attr:`_types`).
1861 :returns: Dictionary of types, indexed by normalized identifier.
1862 """
1863 return self._types
1865 @readonly
1866 def Subtypes(self) -> dict[str, Subtype]:
1867 """
1868 Read-only property to access the subtypes (:attr:`_subtypes`).
1870 :returns: Dictionary of subtypes, indexed by normalized identifier.
1871 """
1872 return self._subtypes
1874 # @readonly
1875 # def Objects(self) -> dict[str, Union[Constant, SharedVariable, Signal, File]]:
1876 # return self._objects
1878 @readonly
1879 def Constants(self) -> dict[str, Constant]:
1880 """
1881 Read-only property to access the constants (:attr:`_constants`).
1883 :returns: Dictionary of constants, indexed by normalized identifier.
1884 """
1885 return self._constants
1887 @readonly
1888 def Signals(self) -> dict[str, Signal]:
1889 """
1890 Read-only property to access the signals (:attr:`_signals`).
1892 :returns: Dictionary of signals, indexed by normalized identifier.
1893 """
1894 return self._signals
1896 @readonly
1897 def SharedVariables(self) -> dict[str, SharedVariable]:
1898 """
1899 Read-only property to access the shared variables (:attr:`_sharedVariables`).
1901 :returns: Dictionary of shared variables, indexed by normalized identifier.
1902 """
1903 return self._sharedVariables
1905 @readonly
1906 def Files(self) -> dict[str, File]:
1907 """
1908 Read-only property to access the files (:attr:`_files`).
1910 :returns: Dictionary of files, indexed by normalized identifier.
1911 """
1912 return self._files
1914 # @readonly
1915 # def Subprograms(self) -> dict[str, Subprogram]:
1916 # return self._subprograms
1918 @readonly
1919 def Functions(self) -> dict[str, list[Function]]:
1920 """
1921 Read-only property to access the functions (:attr:`_functions`).
1923 :returns: Dictionary of functions, indexed by normalized identifier; each entry is a list of overloads.
1924 """
1925 return self._functions
1927 @readonly
1928 def Procedures(self) -> dict[str, list[Procedure]]:
1929 """
1930 Read-only property to access the procedures (:attr:`_procedures`).
1932 :returns: Dictionary of procedures, indexed by normalized identifier; each entry is a list of overloads.
1933 """
1934 return self._procedures
1936 @readonly
1937 def Components(self) -> dict[str, Any]:
1938 """
1939 Read-only property to access the components (:attr:`_components`).
1941 :returns: Dictionary of components, indexed by normalized identifier.
1942 """
1943 return self._components
1945 def _Declarations(self) -> tuple[ModelEntity, ...]:
1946 """
1947 Return all declarations of this declarative region.
1949 A host class with interface items extends the list by its interface items, listed first.
1951 :returns: Tuple of declarations: the declared items, in declaration order.
1952 """
1953 return tuple(self._declaredItems)
1955 def IndexDeclaredItems(self) -> None:
1956 """
1957 Index declared items listed in the concurrent declaration region.
1959 .. rubric:: Algorithm
1961 1. Clear all lookup tables and remove all declared items from :attr:`_namespace`. Interface items and imported
1962 items stay in the namespace.
1963 2. Iterate all declared items:
1965 * Every declared item is added to :attr:`_namespace`, except a protected type body.
1966 * If the declared item is a :class:`~pyVHDLModel.Type.ProtectedTypeBody`, skip it: it completes its protected
1967 type.
1968 * If the declared item is a :class:`~pyVHDLModel.Type.FullType`, then add an entry to :attr:`_types`.
1969 * If the declared item is a :class:`~pyVHDLModel.Type.Subtype`, then add an entry to :attr:`_subtypes`.
1970 * If the declared item is a :class:`~pyVHDLModel.Subprogram.Function`, then add an entry to :attr:`_functions`.
1971 * If the declared item is a :class:`~pyVHDLModel.Subprogram.Procedure`, then add an entry to :attr:`_procedures`.
1972 * If the declared item is a :class:`~pyVHDLModel.Object.Constant`, then add an entry to :attr:`_constants`.
1973 * If the declared item is a :class:`~pyVHDLModel.Object.Signal`, then add an entry to :attr:`_signals`.
1974 * If the declared item is a :class:`~pyVHDLModel.Object.Variable`, TODO.
1975 * If the declared item is a :class:`~pyVHDLModel.Object.SharedVariable`, then add an entry to :attr:`_sharedVariables`.
1976 * If the declared item is a :class:`~pyVHDLModel.Object.File`, then add an entry to :attr:`_files`.
1977 * If the declared item is neither of these types, call :meth:`_IndexOtherDeclaredItem`. |br|
1978 Derived classes may override this virtual function.
1980 Indexing again yields the same lookup tables and namespace.
1982 .. seealso::
1984 :meth:`pyVHDLModel.Design.IndexPackages`
1985 Iterate all packages in the design and index declared items.
1986 :meth:`pyVHDLModel.Library.IndexPackages`
1987 Iterate all packages in the library and index declared items.
1988 :meth:`pyVHDLModel.Library._IndexOtherDeclaredItem`
1989 Iterate all packages in the library and index declared items.
1991 :raises UnhandledCriticalWarningError: If an identifier is declared twice in a declarative region and no
1992 :class:`~pyTooling.Warning.WarningCollector` collects the
1993 :exc:`~pyVHDLModel.Exception.DuplicateDeclarationWarning`.
1994 """
1995 self._types.clear()
1996 self._subtypes.clear()
1997 self._constants.clear()
1998 self._signals.clear()
1999 self._sharedVariables.clear()
2000 self._files.clear()
2001 self._functions.clear()
2002 self._procedures.clear()
2003 self._components.clear()
2004 self._RemoveDeclaredItemsFromNamespace()
2006 for item in self._declaredItems:
2007 self._IndexDeclaredItem(item)
2009 self._areDeclaredItemsIndexed = True
2011 def _IndexDeclaredItem(self, item: ModelEntity) -> None:
2012 """
2013 Add a declared item to the namespace and to the lookup table matching its kind.
2015 :param item: The declared item.
2016 """
2017 from pyVHDLModel.DesignUnit import Component
2018 from pyVHDLModel.Subprogram import Function, Procedure
2019 from pyVHDLModel.Type import Subtype, FullType, ProtectedTypeBody
2021 if isinstance(item, ProtectedTypeBody):
2022 # A protected type body completes its protected type.
2023 pass
2024 elif isinstance(item, FullType):
2025 self._types[item._normalizedIdentifier] = item
2026 self._namespace.AddElement(item._normalizedIdentifier, item)
2027 elif isinstance(item, Subtype):
2028 self._subtypes[item._normalizedIdentifier] = item
2029 self._namespace.AddElement(item._normalizedIdentifier, item)
2030 elif isinstance(item, Function):
2031 # FIXME: overloads are only appended to a list, not matched/resolved by signature (no
2032 # real overload resolution yet).
2033 self._functions.setdefault(item._normalizedIdentifier, []).append(item)
2034 self._namespace.AddElement(item._normalizedIdentifier, item, overloadable=True)
2035 elif isinstance(item, Procedure):
2036 # FIXME: overloads are only appended to a list, not matched/resolved by signature (no
2037 # real overload resolution yet).
2038 self._procedures.setdefault(item._normalizedIdentifier, []).append(item)
2039 self._namespace.AddElement(item._normalizedIdentifier, item, overloadable=True)
2040 elif isinstance(item, Constant):
2041 for normalizedIdentifier in item._normalizedIdentifiers:
2042 self._constants[normalizedIdentifier] = item
2043 self._namespace.AddElement(normalizedIdentifier, item)
2044 # self._objects[normalizedIdentifier] = item
2045 elif isinstance(item, Signal):
2046 for normalizedIdentifier in item._normalizedIdentifiers:
2047 self._signals[normalizedIdentifier] = item
2048 self._namespace.AddElement(normalizedIdentifier, item)
2049 elif isinstance(item, Variable):
2050 # TODO: variables declared in a concurrent declaration region (e.g. shared variables outside a
2051 # protected type) are not yet indexed into a dedicated namespace/lookup table.
2052 identifiers = ", ".join(f"'{i}'" for i in item._identifiers)
2053 WarningCollector.Raise(
2054 NotImplementedWarning(f"IndexDeclaredItems: variable(s) {identifiers} are not yet indexed.", item=item)
2055 )
2056 elif isinstance(item, SharedVariable):
2057 for normalizedIdentifier in item._normalizedIdentifiers:
2058 self._sharedVariables[normalizedIdentifier] = item
2059 self._namespace.AddElement(normalizedIdentifier, item)
2060 elif isinstance(item, File):
2061 for normalizedIdentifier in item._normalizedIdentifiers:
2062 self._files[normalizedIdentifier] = item
2063 self._namespace.AddElement(normalizedIdentifier, item)
2064 elif isinstance(item, Component):
2065 self._components[item._normalizedIdentifier] = item
2066 self._namespace.AddElement(item._normalizedIdentifier, item)
2067 else:
2068 self._IndexOtherDeclaredItem(item)
2070 def _UnindexDeclaredItem(self, item: ModelEntity) -> None:
2071 """
2072 Remove a declared item from the namespace and from the lookup table matching its kind.
2074 :param item: The declared item.
2075 """
2076 from pyVHDLModel.DesignUnit import Component
2077 from pyVHDLModel.Subprogram import Function, Procedure
2078 from pyVHDLModel.Type import Subtype, FullType, ProtectedTypeBody
2080 if isinstance(item, ProtectedTypeBody): 2080 ↛ 2081line 2080 didn't jump to line 2081 because the condition on line 2080 was never true
2081 pass
2082 elif isinstance(item, FullType):
2083 self._RemoveDeclaredItemEntries(self._types, item)
2084 elif isinstance(item, Subtype): 2084 ↛ 2085line 2084 didn't jump to line 2085 because the condition on line 2084 was never true
2085 self._RemoveDeclaredItemEntries(self._subtypes, item)
2086 elif isinstance(item, (Function, Procedure)):
2087 self._UnindexOverload(item)
2088 elif isinstance(item, Constant): 2088 ↛ 2089line 2088 didn't jump to line 2089 because the condition on line 2088 was never true
2089 self._RemoveDeclaredItemEntries(self._constants, item)
2090 elif isinstance(item, Signal):
2091 self._RemoveDeclaredItemEntries(self._signals, item)
2092 elif isinstance(item, Variable): 2092 ↛ 2093line 2092 didn't jump to line 2093 because the condition on line 2092 was never true
2093 pass
2094 elif isinstance(item, SharedVariable): 2094 ↛ 2095line 2094 didn't jump to line 2095 because the condition on line 2094 was never true
2095 self._RemoveDeclaredItemEntries(self._sharedVariables, item)
2096 elif isinstance(item, File): 2096 ↛ 2097line 2096 didn't jump to line 2097 because the condition on line 2096 was never true
2097 self._RemoveDeclaredItemEntries(self._files, item)
2098 elif isinstance(item, Component):
2099 self._RemoveDeclaredItemEntries(self._components, item)
2100 else:
2101 self._UnindexOtherDeclaredItem(item)
2104@export
2105class SequentialDeclarationRegionMixin(DeclarationRegionMixin, mixin=True):
2106 """
2107 A mixin-class for sequential declaration regions: process statements and subprogram bodies.
2109 .. note::
2111 VHDL's ``process_declarative_item`` and ``subprogram_declarative_item`` rules are identical, so both
2112 regions share this implementation. Compared to a concurrent region
2113 (:class:`ConcurrentDeclarationRegionMixin`, ``block_declarative_item``), a sequential region can
2114 declare a **variable**, but no signal, shared variable, component or mode view, and none of the
2115 specifications.
2117 .. seealso::
2119 * :class:`Process statement <pyVHDLModel.Concurrent.ProcessStatement>`
2120 * :class:`Subprogram <pyVHDLModel.Subprogram.Subprogram>`
2121 * :class:`Concurrent declaration region <pyVHDLModel.Regions.ConcurrentDeclarationRegionMixin>`
2122 * :class:`Namespace <pyVHDLModel.Namespace.Namespace>`
2123 """
2125 _declaredItems: list #: List of all declared items in this sequential declaration region.
2126 _namespace: Namespace #: The namespace of this sequential declaration region.
2128 _types: dict[str, FullType] #: Dictionary of all types declared in this sequential declaration region.
2129 _subtypes: dict[str, Subtype] #: Dictionary of all subtypes declared in this sequential declaration region.
2130 _constants: dict[str, Constant] #: Dictionary of all constants declared in this sequential declaration region.
2131 _variables: dict[str, Variable] #: Dictionary of all variables declared in this sequential declaration region.
2132 _files: dict[str, File] #: Dictionary of all files declared in this sequential declaration region.
2133 # FIXME: overloads are only collected into a list, not matched/resolved by signature.
2134 _functions: dict[str, list[Function]] #: Dictionary of all functions declared in this sequential declaration region, indexed by name; each entry is a list of overloads.
2135 _procedures: dict[str, list[Procedure]] #: Dictionary of all procedures declared in this sequential declaration region, indexed by name; each entry is a list of overloads.
2137 def __init__(self, namespaceName: Nullable[str] = None, declaredItems: Nullable[Iterable] = None) -> None:
2138 """
2139 Initialize a sequential declaration region.
2141 :param namespaceName: Name of this region's namespace, usually the host's label or identifier.
2142 :param declaredItems: The items declared in this region.
2143 """
2144 DeclarationRegionMixin.__init__(self)
2146 self._namespace = Namespace(namespaceName)
2148 self._declaredItems = [] # TODO: convert to dict
2149 if declaredItems is not None:
2150 for item in declaredItems:
2151 self._declaredItems.append(item)
2152 item.Parent = self
2154 self._types = {}
2155 self._subtypes = {}
2156 self._constants = {}
2157 self._variables = {}
2158 self._files = {}
2159 self._functions = {}
2160 self._procedures = {}
2162 @readonly
2163 def DeclaredItems(self) -> list:
2164 """
2165 Read-only property to access the declared items (:attr:`_declaredItems`).
2167 :returns: List of declared items.
2168 """
2169 return self._declaredItems
2171 @readonly
2172 def Namespace(self) -> Namespace:
2173 """
2174 Read-only property to access this region's namespace (:attr:`_namespace`).
2176 :returns: The namespace.
2177 """
2178 return self._namespace
2180 @readonly
2181 def Types(self) -> dict[str, FullType]:
2182 """
2183 Read-only property to access the declared types (:attr:`_types`).
2185 :returns: Dictionary of types, indexed by normalized identifier.
2186 """
2187 return self._types
2189 @readonly
2190 def Subtypes(self) -> dict[str, Subtype]:
2191 """
2192 Read-only property to access the declared subtypes (:attr:`_subtypes`).
2194 :returns: Dictionary of subtypes, indexed by normalized identifier.
2195 """
2196 return self._subtypes
2198 @readonly
2199 def Constants(self) -> dict[str, Constant]:
2200 """
2201 Read-only property to access the declared constants (:attr:`_constants`).
2203 :returns: Dictionary of constants, indexed by normalized identifier.
2204 """
2205 return self._constants
2207 @readonly
2208 def Variables(self) -> dict[str, Variable]:
2209 """
2210 Read-only property to access the declared variables (:attr:`_variables`).
2212 :returns: Dictionary of variables, indexed by normalized identifier.
2213 """
2214 return self._variables
2216 @readonly
2217 def Files(self) -> dict[str, File]:
2218 """
2219 Read-only property to access the declared files (:attr:`_files`).
2221 :returns: Dictionary of files, indexed by normalized identifier.
2222 """
2223 return self._files
2225 @readonly
2226 def Functions(self) -> dict[str, list[Function]]:
2227 """
2228 Read-only property to access the declared functions (:attr:`_functions`).
2230 :returns: Dictionary of functions, indexed by normalized identifier; each entry is a list of overloads.
2231 """
2232 return self._functions
2234 @readonly
2235 def Procedures(self) -> dict[str, list[Procedure]]:
2236 """
2237 Read-only property to access the declared procedures (:attr:`_procedures`).
2239 :returns: Dictionary of procedures, indexed by normalized identifier; each entry is a list of overloads.
2240 """
2241 return self._procedures
2243 def _Declarations(self) -> tuple[ModelEntity, ...]:
2244 """
2245 Return all declarations of this declarative region.
2247 A host class with interface items extends the list by its interface items, listed first.
2249 :returns: Tuple of declarations: the declared items, in declaration order.
2250 """
2251 return tuple(self._declaredItems)
2253 def IndexDeclaredItems(self) -> None:
2254 """
2255 Index declared items listed in the sequential declaration region.
2257 All lookup tables are cleared and all declared items are removed from :attr:`_namespace` first. Then every declared
2258 item is added to :attr:`_namespace`, and additionally to the lookup table matching its kind. Items of an unhandled
2259 kind are passed to :meth:`_IndexOtherDeclaredItem`. A protected type body completes its protected type, so it's
2260 skipped. Indexing again yields the same lookup tables and namespace.
2262 .. seealso::
2264 :meth:`ConcurrentDeclarationRegionMixin.IndexDeclaredItems`
2265 The same algorithm for a concurrent declaration region.
2267 :raises UnhandledCriticalWarningError: If an identifier is declared twice in a declarative region and no
2268 :class:`~pyTooling.Warning.WarningCollector` collects the
2269 :exc:`~pyVHDLModel.Exception.DuplicateDeclarationWarning`.
2270 """
2271 self._types.clear()
2272 self._subtypes.clear()
2273 self._constants.clear()
2274 self._variables.clear()
2275 self._files.clear()
2276 self._functions.clear()
2277 self._procedures.clear()
2278 self._RemoveDeclaredItemsFromNamespace()
2280 for item in self._declaredItems:
2281 self._IndexDeclaredItem(item)
2283 self._areDeclaredItemsIndexed = True
2285 def _IndexDeclaredItem(self, item: ModelEntity) -> None:
2286 """
2287 Add a declared item to the namespace and to the lookup table matching its kind.
2289 :param item: The declared item.
2290 """
2291 from pyVHDLModel.Subprogram import Function, Procedure
2292 from pyVHDLModel.Type import Subtype, FullType, ProtectedTypeBody
2294 if isinstance(item, ProtectedTypeBody):
2295 # A protected type body completes its protected type.
2296 pass
2297 elif isinstance(item, FullType):
2298 self._types[item._normalizedIdentifier] = item
2299 self._namespace.AddElement(item._normalizedIdentifier, item)
2300 elif isinstance(item, Subtype):
2301 self._subtypes[item._normalizedIdentifier] = item
2302 self._namespace.AddElement(item._normalizedIdentifier, item)
2303 elif isinstance(item, Function): 2303 ↛ 2306line 2303 didn't jump to line 2306 because the condition on line 2303 was never true
2304 # FIXME: overloads are only appended to a list, not matched/resolved by signature (no
2305 # real overload resolution yet).
2306 self._functions.setdefault(item._normalizedIdentifier, []).append(item)
2307 self._namespace.AddElement(item._normalizedIdentifier, item, overloadable=True)
2308 elif isinstance(item, Procedure):
2309 # FIXME: overloads are only appended to a list, not matched/resolved by signature (no
2310 # real overload resolution yet).
2311 self._procedures.setdefault(item._normalizedIdentifier, []).append(item)
2312 self._namespace.AddElement(item._normalizedIdentifier, item, overloadable=True)
2313 elif isinstance(item, Constant):
2314 for normalizedIdentifier in item._normalizedIdentifiers:
2315 self._constants[normalizedIdentifier] = item
2316 self._namespace.AddElement(normalizedIdentifier, item)
2317 elif isinstance(item, Variable):
2318 for normalizedIdentifier in item._normalizedIdentifiers:
2319 self._variables[normalizedIdentifier] = item
2320 self._namespace.AddElement(normalizedIdentifier, item)
2321 elif isinstance(item, File): 2321 ↛ 2326line 2321 didn't jump to line 2326 because the condition on line 2321 was always true
2322 for normalizedIdentifier in item._normalizedIdentifiers:
2323 self._files[normalizedIdentifier] = item
2324 self._namespace.AddElement(normalizedIdentifier, item)
2325 else:
2326 self._IndexOtherDeclaredItem(item)
2328 def _UnindexDeclaredItem(self, item: ModelEntity) -> None:
2329 """
2330 Remove a declared item from the namespace and from the lookup table matching its kind.
2332 :param item: The declared item.
2333 """
2334 from pyVHDLModel.Subprogram import Function, Procedure
2335 from pyVHDLModel.Type import Subtype, FullType, ProtectedTypeBody
2337 if isinstance(item, ProtectedTypeBody): 2337 ↛ 2338line 2337 didn't jump to line 2338 because the condition on line 2337 was never true
2338 pass
2339 elif isinstance(item, FullType): 2339 ↛ 2340line 2339 didn't jump to line 2340 because the condition on line 2339 was never true
2340 self._RemoveDeclaredItemEntries(self._types, item)
2341 elif isinstance(item, Subtype): 2341 ↛ 2342line 2341 didn't jump to line 2342 because the condition on line 2341 was never true
2342 self._RemoveDeclaredItemEntries(self._subtypes, item)
2343 elif isinstance(item, (Function, Procedure)):
2344 self._UnindexOverload(item)
2345 elif isinstance(item, Constant): 2345 ↛ 2346line 2345 didn't jump to line 2346 because the condition on line 2345 was never true
2346 self._RemoveDeclaredItemEntries(self._constants, item)
2347 elif isinstance(item, Variable): 2347 ↛ 2349line 2347 didn't jump to line 2349 because the condition on line 2347 was always true
2348 self._RemoveDeclaredItemEntries(self._variables, item)
2349 elif isinstance(item, File):
2350 self._RemoveDeclaredItemEntries(self._files, item)
2351 else:
2352 self._UnindexOtherDeclaredItem(item)
2355@export
2356class ProtectedTypeDeclarationRegionMixin(DeclarationRegionMixin, mixin=True):
2357 """
2358 A mixin-class for the declarative region of a protected type declaration.
2360 VHDL's ``protected_type_declarative_item`` rule admits subprogram declarations only, making this the
2361 narrowest declarative region in the language. A protected type *body* differs: its declarative part
2362 matches a subprogram's, so it uses :class:`SequentialDeclarationRegionMixin`.
2364 .. seealso::
2366 * :class:`Protected type <pyVHDLModel.Type.ProtectedType>`
2367 * :class:`Protected type body <pyVHDLModel.Type.ProtectedTypeBody>`
2368 * :class:`Sequential declaration region <pyVHDLModel.Regions.SequentialDeclarationRegionMixin>`
2369 * :class:`Namespace <pyVHDLModel.Namespace.Namespace>`
2370 """
2372 _declaredItems: list #: List of all declared items in this protected type declaration.
2373 _namespace: Namespace #: The namespace of this protected type declaration.
2375 # FIXME: overloads are only collected into a list, not matched/resolved by signature.
2376 _functions: dict[str, list[Function]] #: All declared functions, indexed by name; each is a list of overloads.
2377 _procedures: dict[str, list[Procedure]] #: All declared procedures by name; each is a list of overloads.
2379 def __init__(self, namespaceName: Nullable[str] = None, declaredItems: Nullable[Iterable] = None) -> None:
2380 """
2381 Initialize a protected type declaration region.
2383 :param namespaceName: Name of this region's namespace, usually the protected type's identifier.
2384 :param declaredItems: The items declared in this region.
2385 """
2386 DeclarationRegionMixin.__init__(self)
2388 self._namespace = Namespace(namespaceName)
2390 self._declaredItems = [] # TODO: convert to dict
2391 if declaredItems is not None:
2392 for item in declaredItems:
2393 self._declaredItems.append(item)
2394 item.Parent = self
2396 self._functions = {}
2397 self._procedures = {}
2399 @readonly
2400 def DeclaredItems(self) -> list:
2401 """
2402 Read-only property to access the declared items (:attr:`_declaredItems`).
2404 :returns: List of all declared items.
2405 """
2406 return self._declaredItems
2408 @readonly
2409 def Functions(self) -> dict[str, list[Function]]:
2410 """
2411 Read-only property to access the declared functions (:attr:`_functions`).
2413 :returns: Dictionary of all functions, indexed by name; each entry is a list of overloads.
2414 """
2415 return self._functions
2417 @readonly
2418 def Procedures(self) -> dict[str, list[Procedure]]:
2419 """
2420 Read-only property to access the declared procedures (:attr:`_procedures`).
2422 :returns: Dictionary of all procedures, indexed by name; each entry is a list of overloads.
2423 """
2424 return self._procedures
2426 def _Declarations(self) -> tuple[ModelEntity, ...]:
2427 """
2428 Return all declarations of this declarative region.
2430 A host class with interface items extends the list by its interface items, listed first.
2432 :returns: Tuple of declarations: the declared items, in declaration order.
2433 """
2434 return tuple(self._declaredItems)
2436 def IndexDeclaredItems(self) -> None:
2437 """
2438 Index declared items listed in the protected type declaration.
2440 Both lookup tables are cleared and all declared items are removed from :attr:`_namespace` first. Only subprogram
2441 declarations are legal here, so anything else is passed to :meth:`_IndexOtherDeclaredItem`. Indexing again yields
2442 the same lookup tables and namespace.
2444 .. seealso::
2446 :meth:`SequentialDeclarationRegionMixin.IndexDeclaredItems`
2447 The same algorithm for the protected type's body.
2449 :raises UnhandledCriticalWarningError: If an identifier is declared twice in a declarative region and no
2450 :class:`~pyTooling.Warning.WarningCollector` collects the
2451 :exc:`~pyVHDLModel.Exception.DuplicateDeclarationWarning`.
2452 """
2453 self._functions.clear()
2454 self._procedures.clear()
2455 self._RemoveDeclaredItemsFromNamespace()
2457 for item in self._declaredItems:
2458 self._IndexDeclaredItem(item)
2460 self._areDeclaredItemsIndexed = True
2462 def _IndexDeclaredItem(self, item: ModelEntity) -> None:
2463 """
2464 Add a declared item to the namespace and to the lookup table matching its kind.
2466 :param item: The declared item.
2467 """
2468 from pyVHDLModel.Subprogram import Function, Procedure
2470 if isinstance(item, Function):
2471 # FIXME: overloads are only appended to a list, not matched/resolved by signature (no
2472 # real overload resolution yet).
2473 self._functions.setdefault(item._normalizedIdentifier, []).append(item)
2474 self._namespace.AddElement(item._normalizedIdentifier, item, overloadable=True)
2475 elif isinstance(item, Procedure): 2475 ↛ 2481line 2475 didn't jump to line 2481 because the condition on line 2475 was always true
2476 # FIXME: overloads are only appended to a list, not matched/resolved by signature (no
2477 # real overload resolution yet).
2478 self._procedures.setdefault(item._normalizedIdentifier, []).append(item)
2479 self._namespace.AddElement(item._normalizedIdentifier, item, overloadable=True)
2480 else:
2481 self._IndexOtherDeclaredItem(item)
2483 def _UnindexDeclaredItem(self, item: ModelEntity) -> None:
2484 """
2485 Remove a declared item from the namespace and from the lookup table matching its kind.
2487 :param item: The declared item.
2488 """
2489 from pyVHDLModel.Subprogram import Function, Procedure
2491 if isinstance(item, (Function, Procedure)): 2491 ↛ 2494line 2491 didn't jump to line 2494 because the condition on line 2491 was always true
2492 self._UnindexOverload(item)
2493 else:
2494 self._UnindexOtherDeclaredItem(item)