Coverage for pyVHDLModel/Base.py: 72%
178 statements
« prev ^ index » next coverage.py v7.15.1, created at 2026-07-13 17:58 +0000
« prev ^ index » next coverage.py v7.15.1, created at 2026-07-13 17:58 +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.
35Base-classes for the VHDL language model.
36"""
37from enum import unique, Enum
38from typing import Type, Tuple, Iterable, Optional as Nullable, Union, cast
40from pyTooling.Decorators import export, readonly
41from pyTooling.MetaClasses import ExtendedType
44__all__ = ["ExpressionUnion"]
47ExpressionUnion = Union[
48 'BaseExpression',
49 'QualifiedExpression',
50 'FunctionCall',
51 'TypeConversion',
52 # ConstantOrSymbol, TODO: ObjectSymbol
53 'Literal',
54]
57@export
58@unique
59class Direction(Enum):
60 """An enumeration representing a direction in a range (``to`` or ``downto``)."""
62 To = 0 #: Ascending direction
63 DownTo = 1 #: Descending direction
65 def __str__(self) -> str:
66 """
67 Formats the direction to ``to`` or ``downto``.
69 :returns: Formatted direction.
70 """
71 return ("to", "downto")[cast(int, self.value)] # TODO: check performance
74@export
75@unique
76class Mode(Enum):
77 """
78 A ``Mode`` is an enumeration. It represents the direction of data exchange (``in``, ``out``, ...) for objects in
79 generic, port or parameter lists.
81 In case no *mode* is defined, ``Default`` is used, so the *mode* is inferred from context.
82 """
84 Default = 0 #: Mode not defined, thus it's context dependent.
85 In = 1 #: Input
86 Out = 2 #: Output
87 InOut = 3 #: Bi-directional
88 Buffer = 4 #: Buffered output
89 Linkage = 5 #: undocumented
91 def __str__(self) -> str:
92 """
93 Formats the direction.
95 :returns: Formatted direction.
96 """
97 return ("", "in", "out", "inout", "buffer", "linkage")[cast(int, self.value)] # TODO: check performance
100@export
101class ModelEntity(metaclass=ExtendedType, slots=True):
102 """
103 ``ModelEntity`` is the base-class for all classes in the VHDL language model, except for mixin classes (see multiple
104 inheritance) and enumerations.
106 Each entity in this model has a reference to its parent entity. Therefore, a protected variable :attr:`_parent` is
107 available and a readonly property :attr:`Parent`.
108 """
110 _parent: 'ModelEntity' #: Reference to a parent entity in the logical model hierarchy.
112 def __init__(self, parent: Nullable["ModelEntity"] = None) -> None:
113 """
114 Initializes a VHDL model entity.
116 :param parent: The parent model entity of this entity.
117 """
118 self._parent = parent
120 @property
121 def Parent(self) -> 'ModelEntity':
122 """
123 Property to access the model entity's parent element reference in a logical hierarchy (:attr:`_parent`).
125 :returns: Reference to the parent entity.
126 """
127 return self._parent
129 @Parent.setter
130 def Parent(self, parent: 'ModelEntity') -> None:
131 if parent is None: 131 ↛ 132line 131 didn't jump to line 132 because the condition on line 131 was never true
132 raise ValueError("Parameter 'parent' is None.")
134 self._parent = parent
136 def GetAncestor(self, type: Type) -> 'ModelEntity':
137 parent = self._parent
138 while not isinstance(parent, type):
139 parent = parent._parent
141 return parent
144@export
145class NamedEntityMixin(metaclass=ExtendedType, mixin=True):
146 """
147 A ``NamedEntityMixin`` is a mixin class for all VHDL entities that have an identifier.
149 Protected variables :attr:`_identifier` and :attr:`_normalizedIdentifier` are available to derived classes as well as
150 two readonly properties :attr:`Identifier` and :attr:`NormalizedIdentifier` for public access.
151 """
153 _identifier: str #: The identifier of a model entity.
154 _normalizedIdentifier: str #: The normalized (lower case) identifier of a model entity.
156 def __init__(self, identifier: str) -> None:
157 """
158 Initializes a named entity.
160 :param identifier: Identifier (name) of the model entity.
161 """
162 self._identifier = identifier
163 self._normalizedIdentifier = identifier.lower()
165 @readonly
166 def Identifier(self) -> str:
167 """
168 Returns a model entity's identifier (name).
170 :returns: Name of a model entity.
171 """
172 return self._identifier
174 @readonly
175 def NormalizedIdentifier(self) -> str:
176 """
177 Returns a model entity's normalized identifier (lower case name).
179 :returns: Normalized name of a model entity.
180 """
181 return self._normalizedIdentifier
184@export
185class OptionallyNamedEntityMixin(metaclass=ExtendedType, mixin=True):
186 """
187 A ``OptionallyNamedEntityMixin`` is a mixin class for all VHDL entities that have an optional identifier.
189 Protected variables :attr:`_identifier` and :attr:`_normalizedIdentifier` are available to derived classes as well as
190 two readonly properties :attr:`Identifier` and :attr:`NormalizedIdentifier` for public access.
191 """
193 _identifier: Nullable[str] #: The identifier of a model entity.
194 _normalizedIdentifier: Nullable[str] #: The normalized (lower case) identifier of a model entity.
196 def __init__(self, identifier: Nullable[str]) -> None:
197 """
198 Initializes a named entity.
200 :param identifier: Identifier (name) of the model entity.
201 """
202 self._identifier = identifier
203 self._normalizedIdentifier = identifier.lower() if identifier is not None else None
205 @readonly
206 def Identifier(self) -> Nullable[str]:
207 """
208 Returns a model entity's identifier (name).
210 :returns: Name of a model entity.
211 """
212 return self._identifier
214 @readonly
215 def NormalizedIdentifier(self) -> Nullable[str]:
216 """
217 Returns a model entity's normalized identifier (lower case name).
219 :returns: Normalized name of a model entity.
220 """
221 return self._normalizedIdentifier
224@export
225class MultipleNamedEntityMixin(metaclass=ExtendedType, mixin=True):
226 """
227 A ``MultipleNamedEntityMixin`` is a mixin class for all VHDL entities that declare multiple instances at once by
228 defining multiple identifiers.
230 Protected variables :attr:`_identifiers` and :attr:`_normalizedIdentifiers` are available to derived classes as well
231 as two readonly properties :attr:`Identifiers` and :attr:`NormalizedIdentifiers` for public access.
232 """
234 _identifiers: Tuple[str] #: A list of identifiers.
235 _normalizedIdentifiers: Tuple[str] #: A list of normalized (lower case) identifiers.
237 def __init__(self, identifiers: Iterable[str]) -> None:
238 """
239 Initializes a multiple-named entity.
241 :param identifiers: Sequence of identifiers (names) of the model entity.
242 """
243 self._identifiers = tuple(identifiers)
244 self._normalizedIdentifiers = tuple([identifier.lower() for identifier in identifiers])
246 @readonly
247 def Identifiers(self) -> Tuple[str]:
248 """
249 Returns a model entity's tuple of identifiers (names).
251 :returns: Tuple of identifiers.
252 """
253 return self._identifiers
255 @readonly
256 def NormalizedIdentifiers(self) -> Tuple[str]:
257 """
258 Returns a model entity's tuple of normalized identifiers (lower case names).
260 :returns: Tuple of normalized identifiers.
261 """
262 return self._normalizedIdentifiers
265@export
266class LabeledEntityMixin(metaclass=ExtendedType, mixin=True):
267 """
268 A ``LabeledEntityMixin`` is a mixin class for all VHDL entities that can have labels.
270 protected variables :attr:`_label` and :attr:`_normalizedLabel` are available to derived classes as well as two
271 readonly properties :attr:`Label` and :attr:`NormalizedLabel` for public access.
272 """
273 _label: Nullable[str] #: The label of a model entity.
274 _normalizedLabel: Nullable[str] #: The normalized (lower case) label of a model entity.
276 def __init__(self, label: Nullable[str]) -> None:
277 """
278 Initializes a labeled entity.
280 :param label: Label of the model entity.
281 """
282 self._label = label
283 self._normalizedLabel = label.lower() if label is not None else None
285 @readonly
286 def Label(self) -> Nullable[str]:
287 """
288 Returns a model entity's label.
290 :returns: Label of a model entity.
291 """
292 return self._label
294 @readonly
295 def NormalizedLabel(self) -> Nullable[str]:
296 """
297 Returns a model entity's normalized (lower case) label.
299 :returns: Normalized label of a model entity.
300 """
301 return self._normalizedLabel
304@export
305class DocumentedEntityMixin(metaclass=ExtendedType, mixin=True):
306 """
307 A ``DocumentedEntityMixin`` is a mixin class for all VHDL entities that can have an associated documentation.
309 A protected variable :attr:`_documentation` is available to derived classes as well as a readonly property
310 :attr:`Documentation` for public access.
311 """
313 _documentation: Nullable[str] #: The associated documentation of a model entity.
315 def __init__(self, documentation: Nullable[str]) -> None:
316 """
317 Initializes a documented entity.
319 :param documentation: Documentation of a model entity.
320 """
321 self._documentation = documentation
323 @readonly
324 def Documentation(self) -> Nullable[str]:
325 """
326 Returns a model entity's associated documentation.
328 :returns: Associated documentation of a model entity.
329 """
330 return self._documentation
333@export
334class ConditionalMixin(metaclass=ExtendedType, mixin=True):
335 """A ``ConditionalMixin`` is a mixin-class for all statements with a condition."""
337 _condition: ExpressionUnion
339 def __init__(self, condition: Nullable[ExpressionUnion] = None) -> None:
340 """
341 Initializes a statement with a condition.
343 When the condition is not None, the condition's parent reference is set to this statement.
345 :param condition: The expression representing the condition.
346 """
347 self._condition = condition
348 if condition is not None:
349 condition.Parent = self
351 @readonly
352 def Condition(self) -> ExpressionUnion:
353 """
354 Read-only property to access the condition of a statement (:attr:`_condition`).
356 :returns: The expression representing the condition of a statement.
357 """
358 return self._condition
361@export
362class BranchMixin(metaclass=ExtendedType, mixin=True):
363 """A ``BranchMixin`` is a mixin-class for all statements with branches."""
365 def __init__(self) -> None:
366 pass
369@export
370class ConditionalBranchMixin(BranchMixin, ConditionalMixin, mixin=True):
371 """A ``BaseBranch`` is a mixin-class for all branch statements with a condition."""
372 def __init__(self, condition: ExpressionUnion) -> None:
373 super().__init__()
374 ConditionalMixin.__init__(self, condition)
377@export
378class IfBranchMixin(ConditionalBranchMixin, mixin=True):
379 """A ``BaseIfBranch`` is a mixin-class for all if-branches."""
382@export
383class ElsifBranchMixin(ConditionalBranchMixin, mixin=True):
384 """A ``BaseElsifBranch`` is a mixin-class for all elsif-branches."""
387@export
388class ElseBranchMixin(BranchMixin, mixin=True):
389 """A ``BaseElseBranch`` is a mixin-class for all else-branches."""
392@export
393class ReportStatementMixin(metaclass=ExtendedType, mixin=True):
394 """A ``MixinReportStatement`` is a mixin-class for all report and assert statements."""
396 _message: Nullable[ExpressionUnion]
397 _severity: Nullable[ExpressionUnion]
399 def __init__(self, message: Nullable[ExpressionUnion] = None, severity: Nullable[ExpressionUnion] = None) -> None:
400 self._message = message
401 if message is not None:
402 message.Parent = self
404 self._severity = severity
405 if severity is not None:
406 severity.Parent = self
408 @property
409 def Message(self) -> Nullable[ExpressionUnion]:
410 return self._message
412 @property
413 def Severity(self) -> Nullable[ExpressionUnion]:
414 return self._severity
417@export
418class AssertStatementMixin(ReportStatementMixin, ConditionalMixin, mixin=True):
419 """A ``MixinAssertStatement`` is a mixin-class for all assert statements."""
421 def __init__(self, condition: ExpressionUnion, message: Nullable[ExpressionUnion] = None, severity: Nullable[ExpressionUnion] = None) -> None:
422 super().__init__(message, severity)
423 ConditionalMixin.__init__(self, condition)
426class BlockStatementMixin(metaclass=ExtendedType, mixin=True):
427 """A ``BlockStatement`` is a mixin-class for all block statements."""
429 def __init__(self) -> None:
430 pass
433@export
434class BaseChoice(ModelEntity):
435 """A ``Choice`` is a base-class for all choices."""
438@export
439class BaseCase(ModelEntity):
440 """
441 A ``Case`` is a base-class for all cases.
442 """
445@export
446class Range(ModelEntity):
447 _leftBound: ExpressionUnion
448 _rightBound: ExpressionUnion
449 _direction: Direction
451 def __init__(self, leftBound: ExpressionUnion, rightBound: ExpressionUnion, direction: Direction, parent: Nullable[ModelEntity] = None) -> None:
452 super().__init__(parent)
454 self._leftBound = leftBound
455 leftBound.Parent = self
457 self._rightBound = rightBound
458 rightBound.Parent = self
460 self._direction = direction
462 @property
463 def LeftBound(self) -> ExpressionUnion:
464 return self._leftBound
466 @property
467 def RightBound(self) -> ExpressionUnion:
468 return self._rightBound
470 @property
471 def Direction(self) -> Direction:
472 return self._direction
474 def __str__(self) -> str:
475 return f"{self._leftBound!s} {self._direction!s} {self._rightBound!s}"
478@export
479class WaveformElement(ModelEntity):
480 _expression: ExpressionUnion
481 _after: ExpressionUnion
483 def __init__(self, expression: ExpressionUnion, after: Nullable[ExpressionUnion] = None, parent: Nullable[ModelEntity] = None) -> None:
484 super().__init__(parent)
486 self._expression = expression
487 expression.Parent = self
489 self._after = after
490 if after is not None:
491 after.Parent = self
493 @property
494 def Expression(self) -> ExpressionUnion:
495 return self._expression
497 @property
498 def After(self) -> Expression:
499 return self._after