Coverage for pyVHDLModel/Name.py: 96%
153 statements
« prev ^ index » next coverage.py v7.17.0, created at 2026-10-25 10:05 +0000
« prev ^ index » next coverage.py v7.17.0, created at 2026-10-25 10:05 +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.
35VHDL uses *names* to express cross-references from *usage locations* to *declarations*. Here, *names* are single or
36combined identifiers. :mod:`Symbols <pyVHDLModel.Symbol>` are structures representing a *name* and a reference
37(pointer) to the referenced vhdl language entity.
38"""
39from __future__ import annotations
41from typing import TYPE_CHECKING, Iterable, Optional as Nullable, Self
43from pyTooling.Decorators import export, readonly
44from pyTooling.MetaClasses import abstractclass
46from pyVHDLModel.Base import ModelEntity, ExpressionUnion
47if TYPE_CHECKING: 47 ↛ 48line 47 didn't jump to line 48 because the condition on line 47 was never true
48 from pyVHDLModel.Visitor import Visitor
51@export
52@abstractclass
53class Name(ModelEntity):
54 """
55 ``Name`` is the base-class for all *names* in the VHDL language model.
57 .. seealso::
59 * :class:`Simple name <pyVHDLModel.Name.SimpleName>`
60 * :class:`Parenthesis name <pyVHDLModel.Name.ParenthesisName>`
61 * :class:`Indexed name <pyVHDLModel.Name.IndexedName>`
62 * :class:`Sliced name <pyVHDLModel.Name.SlicedName>`
63 * :class:`Selected name <pyVHDLModel.Name.SelectedName>`
64 * :class:`Attribute name <pyVHDLModel.Name.AttributeName>`
65 * :class:`Open name <pyVHDLModel.Name.OpenName>`
66 """
68 _identifier: str #: The name's identifier.
69 _normalizedIdentifier: str #: The normalized (lower case) identifier.
70 # TODO: seams to be unused. There is no reverse linking, or?
71 _root: Nullable[Name] #: Reference to the root of the name chain.
72 _prefix: Nullable[Name] #: Reference to the name's prefix, or ``None`` for a simple name.
74 def __init__(self, identifier: str, prefix: Nullable[Name] = None, parent: Nullable[ModelEntity] = None) -> None:
75 """
76 Initializes a name.
78 :param identifier: The name's identifier.
79 :param prefix: Reference to the name's prefix, or ``None`` for a simple name.
80 :param parent: The parent model entity of this entity.
81 """
82 super().__init__(parent)
84 self._identifier = identifier
85 self._normalizedIdentifier = identifier.lower()
87 if prefix is None:
88 self._prefix = None
89 self._root = self
90 else:
91 self._prefix = prefix
92 self._root = prefix._root
94 @readonly
95 def Identifier(self) -> str:
96 """
97 Read-only property to access the identifier this name references (:attr:`_identifier`).
99 :returns: The referenced identifier.
100 """
101 return self._identifier
103 @readonly
104 def NormalizedIdentifier(self) -> str:
105 """
106 Read-only property to access the normalized identifier this name references (:attr:`_normalizedIdentifier`).
108 :returns: The referenced identifier (normalized).
109 """
110 return self._normalizedIdentifier
112 @readonly
113 def Root(self) -> Name:
114 """
115 Read-only property to access the root (left-most) element in a chain of names (:attr:`_root`).
117 In case the name is a :class:`simple name <SimpleName>`, the root points to the name itself.
119 :returns: The name's root element.
120 """
121 return self._root
123 @readonly
124 def Prefix(self) -> Nullable[Name]:
125 """
126 Read-only property to access the name's prefix in a chain of names (:attr:`_prefix`).
128 :returns: The name left from current name, if not a simple name, otherwise ``None``.
129 """
130 return self._prefix
132 @readonly
133 def HasPrefix(self) -> bool:
134 """
135 Check if the name has a prefix, i.e. :attr:`_prefix` is set.
137 This is true for all names except :class:`simple names <SimpleName>`.
139 :returns: ``True``, if the name has a prefix.
140 """
141 return self._prefix is not None
143 def Copy(self) -> Self:
144 """
145 Create a copy of this name.
147 The copy has no parent. Its prefix, if any, is a copy of this name's prefix.
149 :returns: The new name.
150 """
151 prefix = None if self._prefix is None else self._prefix.Copy()
152 return self.__class__(self._identifier, prefix)
154 def __repr__(self) -> str:
155 """
156 Formats a representation of the name.
158 **Format:** ``Name: 'sig'``
160 :returns: String representation of the name.
161 """
162 return f"Name: '{self.__str__()}'"
164 def __str__(self) -> str:
165 """
166 Formats the name.
168 **Format:** ``sig``
170 :returns: Formatted name.
171 """
172 return self._identifier
175@export
176class SimpleName(Name):
177 """
178 A *simple name* is a name made from a single word.
180 For example, the entity name in an architecture declaration is a simple name, while the name of the architecture
181 itself is an identifier. The simple name references is again an identifier in the entity declaration, thus names
182 reference other (already) declared language entities.
183 """
185 def Visit(self, visitor: Visitor) -> None:
186 """
187 Walk this simple name with a visitor.
189 :param visitor: The visitor receiving the events.
190 """
191 visitor.VisitSimpleName(self)
192 visitor.DepartSimpleName(self)
195@export
196class ParenthesisName(Name):
197 """
198 Represents a name followed by a parenthesized association list.
200 Used where indexing and a function call are indistinguishable before resolution.
201 """
202 _associations: list #: List of all associations in the parenthesis.
204 def __init__(self, prefix: Name, associations: Iterable, parent: Nullable[ModelEntity] = None) -> None:
205 """
206 Initializes a name followed by a parenthesized association list.
208 :param prefix: Reference to the name's prefix, or ``None`` for a simple name.
209 :param associations: List of all associations in the parenthesis.
210 :param parent: The parent model entity of this entity.
211 """
212 super().__init__("", prefix, parent)
214 self._associations = associationList = []
215 for association in associations:
216 associationList.append(association)
217 association.Parent = self
219 @readonly
220 def Associations(self) -> list:
221 """
222 Read-only property to access the associations (:attr:`_associations`).
224 :returns: List of associations.
225 """
226 return self._associations
228 def Copy(self) -> Self:
229 """
230 Create a copy of this parenthesis name.
232 The copy has no parent. Its prefix and associations are copies of this name's prefix and associations.
234 :returns: The new parenthesis name.
235 """
236 return self.__class__(self._prefix.Copy(), [association.Copy() for association in self._associations])
238 def Visit(self, visitor: Visitor) -> None:
239 """
240 Walk this parenthesis name, its prefix and its associations with a visitor.
242 :param visitor: The visitor receiving the events.
243 """
244 visitor.VisitParenthesisName(self)
245 self._prefix.Visit(visitor)
246 visitor.VisitLeftParenthesis(self)
247 for position, association in enumerate(self._associations):
248 if position > 0:
249 visitor.VisitComma(self)
251 association.Visit(visitor)
253 visitor.DepartParenthesisName(self)
255 def __str__(self) -> str:
256 """
257 Formats the parenthesis name.
259 **Format:** ``func(a, b)``
261 :returns: Formatted parenthesis name.
262 """
263 return f"{self._prefix!s}({', '.join(str(a) for a in self._associations)})"
266@export
267class IndexedName(Name):
268 """
269 Represents a name indexing an array by one or more values.
271 .. admonition:: Example
273 .. code-block:: VHDL
275 s <= v(0);
276 -- ^^^^ <- the indexed name
277 """
278 _indices: list[ExpressionUnion] #: List of all index expressions, one per dimension.
280 def __init__(self, prefix: Name, indices: Iterable[ExpressionUnion], parent: Nullable[ModelEntity] = None) -> None:
281 """
282 Initializes a name indexing an array by one or more values.
284 :param prefix: Reference to the name's prefix, or ``None`` for a simple name.
285 :param indices: List of all index expressions, one per dimension.
286 :param parent: The parent model entity of this entity.
287 """
288 super().__init__("", prefix, parent)
290 self._indices = indexList = []
291 for index in indices:
292 indexList.append(index)
293 index.Parent = self
295 @readonly
296 def Indices(self) -> list[ExpressionUnion]:
297 """
298 Read-only property to access the indices (:attr:`_indices`).
300 :returns: List of indices.
301 """
302 return self._indices
304 def Copy(self) -> Self:
305 """
306 Create a copy of this indexed name.
308 The copy has no parent. Its prefix and indices are copies of this name's prefix and indices.
310 :returns: The new indexed name.
311 """
312 return self.__class__(self._prefix.Copy(), [index.Copy() for index in self._indices])
314 def Visit(self, visitor: Visitor) -> None:
315 """
316 Walk this indexed name, its prefix and its indices with a visitor.
318 :param visitor: The visitor receiving the events.
319 """
320 visitor.VisitIndexedName(self)
321 self._prefix.Visit(visitor)
322 visitor.VisitLeftParenthesis(self)
323 for position, index in enumerate(self._indices):
324 if position > 0:
325 visitor.VisitComma(self)
327 index.Visit(visitor)
329 visitor.DepartIndexedName(self)
331 def __str__(self) -> str:
332 """
333 Formats the indexed name.
335 **Format:** ``arr(0)``
337 :returns: Formatted indexed name.
338 """
339 return f"{self._prefix!s}({', '.join(str(i) for i in self._indices)})"
342@export
343class SlicedName(Name):
344 """
345 Represents a name selecting a slice of an array.
347 .. admonition:: Example
349 .. code-block:: VHDL
351 vres := v(3 downto 0);
352 -- ^^^^^^^^^^^^^ <- the sliced name
353 """
354 def Visit(self, visitor: Visitor) -> None:
355 """
356 Walk this sliced name and its prefix with a visitor.
358 :param visitor: The visitor receiving the events.
359 """
360 visitor.VisitSlicedName(self)
361 if self._prefix is not None:
362 self._prefix.Visit(visitor)
364 visitor.DepartSlicedName(self)
367@export
368class SelectedName(Name):
369 """
370 A *selected name* is a name made from multiple words separated by a dot (``.``).
372 For example, the library and entity name in a direct entity instantiation is a selected name. Here the entity
373 identifier is a selected name. The library identifier is a :class:`simple name <SimpleName>`, which is
374 referenced by the selected name via the :attr:`~pyVHDLModel.Name.Prefix` property.
376 .. seealso::
378 * :class:`All name <pyVHDLModel.Name.AllName>`
379 """
381 def __init__(self, identifier: str, prefix: Name, parent: Nullable[ModelEntity] = None) -> None:
382 """
383 Initializes a selected name.
385 :param identifier: The name's identifier.
386 :param prefix: Reference to the name's prefix, or ``None`` for a simple name.
387 :param parent: The parent model entity of this entity.
388 """
389 super().__init__(identifier, prefix, parent)
391 def Copy(self) -> Self:
392 """
393 Create a copy of this selected name.
395 The copy has no parent. Its prefix is a copy of this name's prefix.
397 :returns: The new selected name.
398 """
399 return self.__class__(self._identifier, self._prefix.Copy())
401 def Visit(self, visitor: Visitor) -> None:
402 """
403 Walk this selected name and its prefix with a visitor.
405 :param visitor: The visitor receiving the events.
406 """
407 visitor.VisitSelectedName(self)
408 self._prefix.Visit(visitor)
409 visitor.DepartSelectedName(self)
411 def __str__(self) -> str:
412 """
413 Formats the selected name.
415 **Format:** ``rec.elem``
417 :returns: Formatted selected name.
418 """
419 return f"{self._prefix!s}.{self._identifier}"
422@export
423class AttributeName(Name):
424 """
425 Represents a name selecting an attribute of its prefix.
427 .. admonition:: Example
429 .. code-block:: VHDL
431 for i in v'range loop
432 -- ^^^^^^^ <- the attribute name
433 """
434 def __init__(self, identifier: str, prefix: Name, parent: Nullable[ModelEntity] = None) -> None:
435 """
436 Initializes a name selecting an attribute of its prefix.
438 :param identifier: The name's identifier.
439 :param prefix: Reference to the name's prefix, or ``None`` for a simple name.
440 :param parent: The parent model entity of this entity.
441 """
442 super().__init__(identifier, prefix, parent)
444 def Copy(self) -> Self:
445 """
446 Create a copy of this attribute name.
448 The copy has no parent. Its prefix is a copy of this name's prefix.
450 :returns: The new attribute name.
451 """
452 return self.__class__(self._identifier, self._prefix.Copy())
454 def Visit(self, visitor: Visitor) -> None:
455 """
456 Walk this attribute name and its prefix with a visitor.
458 :param visitor: The visitor receiving the events.
459 """
460 visitor.VisitAttributeName(self)
461 self._prefix.Visit(visitor)
462 visitor.DepartAttributeName(self)
464 def __str__(self) -> str:
465 """
466 Formats the attribute name.
468 **Format:** ``v'range``
470 :returns: Formatted attribute name.
471 """
472 return f"{self._prefix!s}'{self._identifier}"
475@export
476class AllName(SelectedName):
477 """
478 The *all name* represents the reserved word ``all`` used in names.
480 Most likely this name is used in use-statements.
481 """
482 def __init__(self, prefix: Name, parent: Nullable[ModelEntity] = None) -> None:
483 """
484 Initializes an ``all`` name.
486 :param prefix: Reference to the name's prefix, or ``None`` for a simple name.
487 :param parent: The parent model entity of this entity.
488 """
489 super().__init__("all", prefix, parent) # TODO: the case of 'ALL' is not preserved
491 def Copy(self) -> Self:
492 """
493 Create a copy of this ``all`` name.
495 The copy has no parent. Its prefix is a copy of this name's prefix.
497 :returns: The new ``all`` name.
498 """
499 return self.__class__(self._prefix.Copy())
501 def Visit(self, visitor: Visitor) -> None:
502 """
503 Walk this ``all`` name and its prefix (if any) with a visitor.
505 :param visitor: The visitor receiving the events.
506 """
507 visitor.VisitAllName(self)
508 if self._prefix is not None:
509 self._prefix.Visit(visitor)
511 visitor.DepartAllName(self)
514@export
515class OpenName(Name):
516 """
517 The *open name* represents the reserved word ``open``.
519 Most likely this name is used in port associations.
520 """
521 def __init__(self, parent: Nullable[ModelEntity] = None) -> None:
522 """
523 Initializes an ``open`` name.
525 :param parent: The parent model entity of this entity.
526 """
527 super().__init__("open", parent=parent) # TODO: the case of 'OPEN' is not preserved
529 def Copy(self) -> Self:
530 """
531 Create a copy of this ``open`` name.
533 The copy has no parent.
535 :returns: The new ``open`` name.
536 """
537 return self.__class__()
539 def Visit(self, visitor: Visitor) -> None:
540 """
541 Walk this ``open`` name with a visitor.
543 :param visitor: The visitor receiving the events.
544 """
545 visitor.VisitOpenName(self)
546 visitor.DepartOpenName(self)
548 def __str__(self) -> str:
549 """
550 Formats the open name.
552 **Format:** ``open``
554 :returns: Formatted open name.
555 """
556 return "open"