Coverage for pyVHDLModel/Common.py: 96%
299 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.
35Common definitions and Mixins are used by many classes in the model as base-classes.
36"""
37from __future__ import annotations
39from typing import TYPE_CHECKING, Iterable, Union, Optional as Nullable
41from pyTooling.Common import getFullyQualifiedName, getTypeName
42from pyTooling.Decorators import export, readonly
43from pyTooling.MetaClasses import ExtendedType, abstractclass
45from pyVHDLModel.Base import ModelEntity, LabeledEntityMixin, BaseCase, BaseChoice, WaveformElement, \
46 ConditionalMixin, ChoicesMixin
47from pyVHDLModel.Exception import AlreadyInDesignError, NotAChildError, AssociationOrderError, \
48 DuplicateParameterAssociationError
49from pyVHDLModel.Expression import BaseExpression, QualifiedExpression, FunctionCall, TypeConversion, Literal
50from pyVHDLModel.Name import SimpleName
51from pyVHDLModel.Symbol import Symbol, SignalSymbol, VariableSymbol
52from pyVHDLModel.Association import ParameterAssociationItem
54if TYPE_CHECKING: 54 ↛ 55line 54 didn't jump to line 55 because the condition on line 54 was never true
55 from pyVHDLModel.Visitor import Visitor
58ExpressionUnion = Union[
59 BaseExpression,
60 QualifiedExpression,
61 FunctionCall,
62 TypeConversion,
63 # ConstantOrSymbol, TODO: ObjectSymbol
64 Literal,
65]
68@export
69class AllowBlackboxMixin(metaclass=ExtendedType, mixin=True, expects=("_parent",)):
70 """
71 A mixin-class for language entities that may permit blackboxes.
73 The setting is inherited from the parent when not set locally (:data:`AllowBlackbox`). The host class provides the
74 reference to the parent ``_parent``.
76 .. seealso::
78 * :class:`Concurrent block statement <pyVHDLModel.Concurrent.ConcurrentBlockStatement>`
79 * :class:`Generate branch <pyVHDLModel.Concurrent.GenerateBranch>`
80 * :class:`Generate statement <pyVHDLModel.Concurrent.GenerateStatement>`
81 * :class:`Concurrent case <pyVHDLModel.Concurrent.ConcurrentCase>`
82 * :class:`Package <pyVHDLModel.DesignUnit.Package>`
83 * :class:`Entity <pyVHDLModel.DesignUnit.Entity>`
84 * :class:`Architecture <pyVHDLModel.DesignUnit.Architecture>`
85 * :class:`Component <pyVHDLModel.DesignUnit.Component>`
86 * :class:`Design <pyVHDLModel.Design>`
87 * :class:`Library <pyVHDLModel.Library>`
88 """
89 _allowBlackbox: Nullable[bool] #: Allow blackboxes for components; ``None`` inherits the parent's setting.
91 def __init__(self, allowBlackbox: Nullable[bool] = None) -> None:
92 """
93 Initializes a hierarchical model entity allow for blackboxes.
95 :param allowBlackbox: Allow blackboxes for components in language entity.
96 """
97 self._allowBlackbox = allowBlackbox
99 @property
100 def AllowBlackbox(self) -> bool:
101 """
102 Property to return whether a design supports blackboxes, inherited from the parent if not set locally
103 (:attr:`_allowBlackbox`).
105 .. rubric:: Algorithm
107 1. If allow blackbox property is locally set, return the local value,
108 2. Otherwise, return allow blackbox value from parent object.
110 :returns: ``True``, if blackboxes are allowed.
111 :raises VHDLModelException: If neither a local value is set nor a parent object is available to inherit the
112 value from.
113 """
114 if self._allowBlackbox is not None:
115 return self._allowBlackbox
116 elif self._parent is None:
117 from pyVHDLModel.Exception import VHDLModelException
119 raise VHDLModelException(f"AllowBlackbox is not set on {self!r} and no parent is available to inherit it from.")
120 else:
121 return self._parent.AllowBlackbox
123 @AllowBlackbox.setter
124 def AllowBlackbox(self, value: Nullable[bool]) -> None:
125 self._allowBlackbox = value
128@export
129@abstractclass
130class Statement(ModelEntity, LabeledEntityMixin):
131 """
132 A ``Statement`` is a base-class for all statements.
134 .. seealso::
136 * :class:`Concurrent statement <pyVHDLModel.Concurrent.ConcurrentStatement>`
137 * :class:`Sequential statement <pyVHDLModel.Sequential.SequentialStatement>`
138 """
139 def __init__(self, label: Nullable[str] = None, parent=None) -> None:
140 """
141 Initializes a statement.
143 :param label: The label of a model entity.
144 :param parent: The parent model entity of this entity.
145 """
146 super().__init__(parent)
147 LabeledEntityMixin.__init__(self, label)
150@export
151class ProcedureCallMixin(metaclass=ExtendedType, mixin=True):
152 """
153 A mixin-class for statements calling a procedure.
155 The called procedure is available as :data:`Procedure`, its actual parameters as
156 :data:`ParameterAssociationItems`.
158 An association declares nothing: the mutators update neither a namespace nor a lookup table of the host class, so the
159 mixin expects nothing from its host.
161 .. seealso::
163 * :class:`Concurrent procedure call <pyVHDLModel.Concurrent.ConcurrentProcedureCall>`
164 * :class:`Sequential procedure call <pyVHDLModel.Sequential.SequentialProcedureCall>`
165 """
166 # TODO: implement a ProcedureSymbol
167 _procedure: Symbol #: Reference to the called procedure.
168 _parameterAssociationItems: list[ParameterAssociationItem] #: List of all parameter associations of the call.
170 def __init__(self, procedureName: Symbol, parameterAssociationItems: Nullable[Iterable[ParameterAssociationItem]] = None) -> None:
171 """
172 Initializes a procedure call.
174 :param procedureName: Reference to the called procedure.
175 :param parameterAssociationItems: List of all parameter associations of the call.
176 """
177 self._procedure = procedureName
178 procedureName.Parent = self
180 # TODO: extract to mixin
181 self._parameterAssociationItems = parameterAssociationItemList = []
182 if parameterAssociationItems is not None:
183 for parameterMapping in parameterAssociationItems:
184 parameterAssociationItemList.append(parameterMapping)
185 parameterMapping.Parent = self
187 def _VisitProcedureCall(self, visitor: Visitor) -> None:
188 """
189 Walk the called procedure and the actual parameter part, if there are parameters, with a visitor.
191 :param visitor: The visitor receiving the events.
192 """
193 self._procedure.Visit(visitor)
194 if len(self._parameterAssociationItems) == 0:
195 return
197 visitor.VisitActualParameterPart(self)
198 for index, item in enumerate(self._parameterAssociationItems):
199 if index > 0:
200 visitor.VisitComma(self)
202 item.Visit(visitor)
204 visitor.DepartActualParameterPart(self)
206 @readonly
207 def Procedure(self) -> Symbol:
208 """
209 Read-only property to access the procedure (:attr:`_procedure`).
211 :returns: The procedure.
212 """
213 return self._procedure
215 @readonly
216 def ParameterAssociationItems(self) -> list[ParameterAssociationItem]:
217 """
218 Read-only property to access the parameter association items (:attr:`_parameterAssociationItems`).
220 :returns: List of parameter association items.
221 """
222 return self._parameterAssociationItems
224 def AddParameterAssociationItem(self, parameterAssociationItem: ParameterAssociationItem) -> None:
225 """
226 Add a parameter association at the end of the parameter association list.
228 The association's parent is set to this procedure call.
230 :param parameterAssociationItem: The parameter association to add.
231 :raises ValueError: If parameter 'parameterAssociationItem' is ``None``.
232 :raises TypeError: If parameter 'parameterAssociationItem' is not of type
233 :class:`~pyVHDLModel.Association.ParameterAssociationItem`.
234 :raises AlreadyInDesignError: If parameter 'parameterAssociationItem' already has a parent.
235 :raises DuplicateParameterAssociationError: If the association is named, and another named association of the
236 parameter association list has the same formal.
237 :raises AssociationOrderError: If the association is positional, and the parameter association list has
238 a named association.
240 .. seealso::
242 :meth:`InsertParameterAssociationItem`
243 |rarr| Insert a parameter association at a position of the parameter association list.
244 :meth:`RemoveParameterAssociationItem`
245 |rarr| Remove a parameter association from the parameter association list.
246 """
247 self.InsertParameterAssociationItem(len(self._parameterAssociationItems), parameterAssociationItem)
249 def InsertParameterAssociationItem(self, index: int, parameterAssociationItem: ParameterAssociationItem) -> None:
250 """
251 Insert a parameter association at a position of the parameter association list.
253 The association's parent is set to this procedure call.
255 A named association (``formal => actual``) can't repeat the formal of another named association of the parameter
256 association list. Only formals given as a simple name are compared: a partial association (``a(0) => x``) or a
257 conversion function (``to_integer(a) => x``) is accepted. All positional associations precede the named
258 associations.
260 :param index: Position, from ``0`` (first) to the number of parameter associations
261 (append). A negative index counts from the end, as for
262 :meth:`list.insert`: ``-1`` inserts before the last parameter
263 association.
264 :param parameterAssociationItem: The parameter association to insert.
265 :raises ValueError: If parameter 'index' is ``None``.
266 :raises TypeError: If parameter 'index' is not of type :class:`int`.
267 :raises ValueError: If parameter 'index' is out of range.
268 :raises ValueError: If parameter 'parameterAssociationItem' is ``None``.
269 :raises TypeError: If parameter 'parameterAssociationItem' is not of type
270 :class:`~pyVHDLModel.Association.ParameterAssociationItem`.
271 :raises AlreadyInDesignError: If parameter 'parameterAssociationItem' already has a parent.
272 :raises DuplicateParameterAssociationError: If the association is named, and another named association of the
273 parameter association list has the same formal.
274 :raises AssociationOrderError: If the association is positional, and a named association precedes the
275 position.
276 :raises AssociationOrderError: If the association is named, and a positional association follows the
277 position.
279 .. seealso::
281 :meth:`AddParameterAssociationItem`
282 |rarr| Add a parameter association at the end of the parameter association list.
283 :meth:`RemoveParameterAssociationItem`
284 |rarr| Remove a parameter association from the parameter association list.
285 """
286 count = len(self._parameterAssociationItems)
287 if index is None: 287 ↛ 288line 287 didn't jump to line 288 because the condition on line 287 was never true
288 raise ValueError("Parameter 'index' is None.")
289 elif not isinstance(index, int): 289 ↛ 290line 289 didn't jump to line 290 because the condition on line 289 was never true
290 ex = TypeError("Parameter 'index' is not of type 'int'.")
291 ex.add_note(f"Got type '{getFullyQualifiedName(index)}'.")
292 raise ex
293 elif not (-count <= index <= count): 293 ↛ 294line 293 didn't jump to line 294 because the condition on line 293 was never true
294 ex = ValueError(f"Parameter 'index' is out of range {-count}..{count}.")
295 ex.add_note(f"Got value '{index}'.")
296 raise ex
298 if parameterAssociationItem is None:
299 raise ValueError("Parameter 'parameterAssociationItem' is None.")
300 elif not isinstance(parameterAssociationItem, ParameterAssociationItem):
301 ex = TypeError("Parameter 'parameterAssociationItem' is not of type 'ParameterAssociationItem'.")
302 ex.add_note(f"Got type '{getFullyQualifiedName(parameterAssociationItem)}'.")
303 raise ex
304 elif parameterAssociationItem._parent is not None:
305 ex = AlreadyInDesignError(
306 f"{getTypeName(parameterAssociationItem)} already has a parent.",
307 element=parameterAssociationItem,
308 parent=parameterAssociationItem._parent
309 )
310 ex.add_note(f"Remove it from its {getTypeName(parameterAssociationItem._parent)} first.")
311 raise ex
313 formal = parameterAssociationItem._formal
314 if isinstance(formal, SimpleName):
315 normalizedFormal = formal._normalizedIdentifier
316 for existing in self._parameterAssociationItems:
317 if isinstance(existing._formal, SimpleName) and existing._formal._normalizedIdentifier == normalizedFormal:
318 ex = DuplicateParameterAssociationError(
319 f"{getTypeName(self)} already associates parameter '{formal._identifier}'.",
320 identifier=formal._identifier,
321 parent=self,
322 element=parameterAssociationItem,
323 existing=existing
324 )
325 ex.add_note(f"Existing association is '{existing!s}'.")
326 raise ex
328 position = index if index >= 0 else count + index
329 if formal is None:
330 for existing in self._parameterAssociationItems[:position]:
331 if existing._formal is not None:
332 ex = AssociationOrderError(
333 f"Positional parameter association can't follow named association '{existing!s}'.",
334 parent=self,
335 element=parameterAssociationItem,
336 existing=existing
337 )
338 ex.add_note("Insert it before the first named association, or name its formal.")
339 raise ex
340 else:
341 for existing in self._parameterAssociationItems[position:]:
342 if existing._formal is None: 342 ↛ 341line 342 didn't jump to line 341 because the condition on line 342 was always true
343 ex = AssociationOrderError(
344 f"Named parameter association can't precede positional association '{existing!s}'.",
345 parent=self,
346 element=parameterAssociationItem,
347 existing=existing
348 )
349 ex.add_note("Insert it after the last positional association.")
350 raise ex
352 self._parameterAssociationItems.insert(index, parameterAssociationItem)
353 parameterAssociationItem.Parent = self
355 def RemoveParameterAssociationItem(self, parameterAssociationItem: ParameterAssociationItem) -> None:
356 """
357 Remove a parameter association from the parameter association list.
359 The association is detached: its parent is cleared. The association itself is unchanged and can be added to another
360 procedure call, or inserted at another position.
362 :param parameterAssociationItem: The parameter association to remove.
363 :raises ValueError: If parameter 'parameterAssociationItem' is ``None``.
364 :raises TypeError: If parameter 'parameterAssociationItem' is not of type
365 :class:`~pyVHDLModel.Association.ParameterAssociationItem`.
366 :raises NotAChildError: If parameter 'parameterAssociationItem' is not a parameter association of this
367 procedure call.
369 .. seealso::
371 :meth:`AddParameterAssociationItem`
372 |rarr| Add a parameter association at the end of the parameter association list.
373 :meth:`InsertParameterAssociationItem`
374 |rarr| Insert a parameter association at a position of the parameter association list.
375 """
376 if parameterAssociationItem is None:
377 raise ValueError("Parameter 'parameterAssociationItem' is None.")
378 elif not isinstance(parameterAssociationItem, ParameterAssociationItem):
379 ex = TypeError("Parameter 'parameterAssociationItem' is not of type 'ParameterAssociationItem'.")
380 ex.add_note(f"Got type '{getFullyQualifiedName(parameterAssociationItem)}'.")
381 raise ex
383 for index, item in enumerate(self._parameterAssociationItems):
384 if item is parameterAssociationItem: 384 ↛ 383line 384 didn't jump to line 383 because the condition on line 384 was always true
385 break
386 else:
387 raise NotAChildError(
388 f"{getTypeName(parameterAssociationItem)} is not a parameter association of this {getTypeName(self)}.",
389 element=parameterAssociationItem,
390 parent=self
391 )
393 del self._parameterAssociationItems[index]
394 parameterAssociationItem._parent = None
397@export
398class AssignmentMixin(metaclass=ExtendedType, mixin=True):
399 """
400 A mixin-class for all assignment statements.
402 .. seealso::
404 * :class:`Signal assignment mixin <pyVHDLModel.Common.SignalAssignmentMixin>`
405 * :class:`Variable assignment mixin <pyVHDLModel.Common.VariableAssignmentMixin>`
406 * :class:`Conditional variable assignment <pyVHDLModel.Sequential.SequentialConditionalVariableAssignment>`
407 * :class:`Sequential selected variable assignment <pyVHDLModel.Sequential.SequentialSelectedVariableAssignment>`
408 """
410 _target: Symbol #: Reference to the assignment's destination.
412 def __init__(self, target: Symbol) -> None:
413 """
414 Initializes an assignment.
416 :param target: Reference to the assignment's destination.
417 """
418 self._target = target
419 target.Parent = self
421 @readonly
422 def Target(self) -> Symbol:
423 """
424 Read-only property to access the target (:attr:`_target`).
426 :returns: The target.
427 """
428 return self._target
431@export
432class SignalAssignmentMixin(AssignmentMixin, mixin=True):
433 """
434 A mixin-class for all signal assignment statements.
436 .. seealso::
438 * :class:`Concurrent signal assignment <pyVHDLModel.Concurrent.ConcurrentSignalAssignment>`
439 * :class:`Sequential signal assignment <pyVHDLModel.Sequential.SequentialSignalAssignment>`
440 * :class:`Conditional signal assignment <pyVHDLModel.Sequential.SequentialConditionalSignalAssignment>`
441 * :class:`Sequential selected signal assignment <pyVHDLModel.Sequential.SequentialSelectedSignalAssignment>`
442 * :class:`Signal force assignment <pyVHDLModel.Sequential.SignalForceAssignment>`
443 * :class:`Signal release assignment <pyVHDLModel.Sequential.SignalReleaseAssignment>`
444 """
446 @readonly
447 def Target(self) -> SignalSymbol:
448 """
449 Read-only property to access the target (:attr:`_target`).
451 :returns: The target.
452 """
453 return self._target
456@export
457class VariableAssignmentMixin(AssignmentMixin, mixin=True):
458 """
459 A mixin-class for all variable assignment statements.
461 .. seealso::
463 * :class:`Sequential variable assignment <pyVHDLModel.Sequential.SequentialVariableAssignment>`
464 """
466 # FIXME: move to sequential?
467 _expression: ExpressionUnion #: The assigned expression.
469 def __init__(self, target: VariableSymbol, expression: ExpressionUnion) -> None:
470 """
471 Initializes a variable assignment.
473 :param target: Reference to the assignment's destination.
474 :param expression: The assigned expression.
475 """
476 super().__init__(target)
478 self._expression = expression
479 expression.Parent = self
481 @readonly
482 def Target(self) -> VariableSymbol:
483 """
484 Read-only property to access the target (:attr:`_target`).
486 :returns: The target.
487 """
488 return self._target
490 @readonly
491 def Expression(self) -> ExpressionUnion:
492 """
493 Read-only property to access the expression (:attr:`_expression`).
495 :returns: The expression.
496 """
497 return self._expression
500@export
501class WaveformMixin(metaclass=ExtendedType, mixin=True):
502 """
503 A mixin-class for all statements/entities holding a waveform (a list of :class:`WaveformElement`).
505 .. seealso::
507 * :class:`Conditional waveform <pyVHDLModel.Common.ConditionalWaveform>`
508 * :class:`Selected waveform <pyVHDLModel.Common.SelectedWaveform>`
509 * :class:`Others selected waveform <pyVHDLModel.Common.OthersSelectedWaveform>`
510 * :class:`Concurrent simple signal assignment <pyVHDLModel.Concurrent.ConcurrentSimpleSignalAssignment>`
511 * :class:`Sequential simple signal assignment <pyVHDLModel.Sequential.SequentialSimpleSignalAssignment>`
512 * :class:`Waveform element <pyVHDLModel.Base.WaveformElement>`
513 """
515 _waveform: list[WaveformElement] #: List of all waveform elements, in the order they were written.
517 def __init__(self, waveform: Iterable[WaveformElement]) -> None:
518 """
519 Initializes a waveform.
521 :param waveform: List of all waveform elements, in the order they were written.
522 """
523 self._waveform = waveformList = []
524 for waveformElement in waveform:
525 waveformList.append(waveformElement)
526 waveformElement.Parent = self
528 def _VisitWaveform(self, visitor: Visitor) -> None:
529 """
530 Walk the waveform elements, separated by commas, with a visitor.
532 :param visitor: The visitor receiving the events.
533 """
534 for index, element in enumerate(self._waveform):
535 if index > 0:
536 visitor.VisitComma(self)
538 element.Visit(visitor)
540 @readonly
541 def Waveform(self) -> list[WaveformElement]:
542 """
543 Read-only property to access the waveform (:attr:`_waveform`).
545 :returns: List of waveform.
546 """
547 return self._waveform
550@export
551class ExpressionMixin(metaclass=ExtendedType, mixin=True):
552 """
553 A mixin-class for all statements/entities holding a single expression.
555 .. seealso::
557 * :class:`Conditional expression <pyVHDLModel.Common.ConditionalExpression>`
558 * :class:`Selected expression <pyVHDLModel.Common.SelectedExpression>`
559 * :class:`Others selected expression <pyVHDLModel.Common.OthersSelectedExpression>`
560 * :class:`Concurrent selected signal assignment <pyVHDLModel.Concurrent.ConcurrentSelectedSignalAssignment>`
561 * :class:`Sequential selected variable assignment <pyVHDLModel.Sequential.SequentialSelectedVariableAssignment>`
562 * :class:`Sequential selected signal assignment <pyVHDLModel.Sequential.SequentialSelectedSignalAssignment>`
563 * :class:`Signal force assignment <pyVHDLModel.Sequential.SignalForceAssignment>`
564 """
566 _expression: ExpressionUnion #: The expression held by this construct.
568 def __init__(self, expression: ExpressionUnion) -> None:
569 """
570 Initializes an expression.
572 :param expression: The expression held by this construct.
573 """
574 self._expression = expression
575 expression.Parent = self
577 @readonly
578 def Expression(self) -> ExpressionUnion:
579 """
580 Read-only property to access the expression (:attr:`_expression`).
582 :returns: The expression.
583 """
584 return self._expression
587@export
588class ConditionalWaveform(ModelEntity, WaveformMixin, ConditionalMixin):
589 """
590 Represents one branch of a conditional signal assignment.
592 Each branch pairs a waveform (:data:`Waveform`) with a condition (:data:`Condition`). The final
593 branch has no ``when``, so its condition is ``None``.
595 .. admonition:: Example
597 .. code-block:: VHDL
599 s <= '1' when cond else '0';
600 -- ^^^^^^^^^^^^^ <- this branch: Waveform=['1'], Condition=cond
601 -- ^^^ <- final branch (no ``when``): Waveform=['0'], Condition=None
603 .. seealso::
605 * :class:`Waveform element <pyVHDLModel.Base.WaveformElement>`
606 * :class:`Conditional expression <pyVHDLModel.Common.ConditionalExpression>`
607 """
609 def __init__(
610 self,
611 waveform: Iterable[WaveformElement],
612 condition: Nullable[ExpressionUnion] = None,
613 parent: Nullable[ModelEntity] = None
614 ) -> None:
615 """
616 Initializes a conditional waveform.
618 :param waveform: List of all waveform elements, in the order they were written.
619 :param condition: The condition selecting this alternative.
620 :param parent: The parent model entity of this entity.
621 """
622 super().__init__(parent)
623 WaveformMixin.__init__(self, waveform)
624 ConditionalMixin.__init__(self, condition)
626 def Visit(self, visitor: Visitor) -> None:
627 """
628 Walk this conditional waveform, its waveform and its condition (if any) with a visitor.
630 :param visitor: The visitor receiving the events.
631 """
632 visitor.VisitConditionalWaveform(self)
633 self._VisitWaveform(visitor)
634 if self._condition is not None:
635 visitor.VisitWhen(self)
636 self._condition.Visit(visitor)
638 visitor.DepartConditionalWaveform(self)
641@export
642class ConditionalExpression(ModelEntity, ExpressionMixin, ConditionalMixin):
643 """
644 Represents one branch of a conditional variable assignment.
646 Each branch pairs an expression (:data:`Expression`) with a condition (:data:`Condition`). The
647 final branch has no ``when``, so its condition is ``None``.
649 .. admonition:: Example
651 .. code-block:: VHDL
653 v := '1' when cond else '0';
654 -- ^^^^^^^^^^^^^ <- this branch: Expression='1', Condition=cond
655 -- ^^^ <- final branch (no ``when``): Expression='0', Condition=None
657 .. seealso::
659 * :class:`Conditional waveform <pyVHDLModel.Common.ConditionalWaveform>`
660 """
662 def __init__(
663 self,
664 expression: ExpressionUnion,
665 condition: Nullable[ExpressionUnion] = None,
666 parent: Nullable[ModelEntity] = None
667 ) -> None:
668 """
669 Initializes a conditional expression.
671 :param expression: The value assigned when the condition holds.
672 :param condition: The condition selecting this alternative.
673 :param parent: The parent model entity of this entity.
674 """
675 super().__init__(parent)
676 ExpressionMixin.__init__(self, expression)
677 ConditionalMixin.__init__(self, condition)
679 def Visit(self, visitor: Visitor) -> None:
680 """
681 Walk this conditional expression, its value and its condition (if any) with a visitor.
683 :param visitor: The visitor receiving the events.
684 """
685 visitor.VisitConditionalExpression(self)
686 self._expression.Visit(visitor)
687 if self._condition is not None:
688 visitor.VisitWhen(self)
689 self._condition.Visit(visitor)
691 visitor.DepartConditionalExpression(self)
694@export
695class ConditionalWaveformsMixin(metaclass=ExtendedType, mixin=True):
696 """
697 A mixin-class for all statements holding a list of :class:`ConditionalWaveform` (both the
698 concurrent and sequential forms of a conditional signal assignment).
700 .. seealso::
702 * :class:`Conditional signal assignment <pyVHDLModel.Concurrent.ConcurrentConditionalSignalAssignment>`
703 * :class:`Conditional signal assignment <pyVHDLModel.Sequential.SequentialConditionalSignalAssignment>` """
705 _conditionalWaveforms: list[ConditionalWaveform] #: All alternatives, in order.
707 def __init__(self, conditionalWaveforms: Iterable[ConditionalWaveform]) -> None:
708 """
709 Initializes conditional waveforms.
711 :param conditionalWaveforms: All alternatives, in order.
712 """
713 self._conditionalWaveforms = conditionalWaveformList = []
714 for conditionalWaveform in conditionalWaveforms:
715 conditionalWaveformList.append(conditionalWaveform)
716 conditionalWaveform.Parent = self
718 def _VisitConditionalWaveforms(self, visitor: Visitor) -> None:
719 """
720 Walk the conditional waveforms, separated by the reserved word ``else``, with a visitor.
722 :param visitor: The visitor receiving the events.
723 """
724 for index, conditionalWaveform in enumerate(self._conditionalWaveforms):
725 if index > 0:
726 visitor.VisitElse(self)
728 conditionalWaveform.Visit(visitor)
730 @readonly
731 def ConditionalWaveforms(self) -> list[ConditionalWaveform]:
732 """
733 Read-only property to access the conditional waveforms (:attr:`_conditionalWaveforms`).
735 :returns: List of conditional waveforms.
736 """
737 return self._conditionalWaveforms
740@export
741class SelectedWaveform(BaseCase, WaveformMixin, ChoicesMixin):
742 """
743 Represents one alternative of a selected signal assignment.
745 Each alternative pairs a waveform (:data:`Waveform`) with the choices selecting it
746 (:data:`Choices`).
748 .. admonition:: Example
750 .. code-block:: VHDL
752 with sel select s <= '1' when '0', '0' when others;
753 -- ^^^^^^^^^^^^ <- this alternative: Choices=['0'], Waveform=['1']
755 .. seealso::
757 * :class:`Waveform element <pyVHDLModel.Base.WaveformElement>`
758 * :class:`Selected expression <pyVHDLModel.Common.SelectedExpression>`
759 """
761 def __init__(
762 self,
763 choices: Iterable[BaseChoice],
764 waveform: Iterable[WaveformElement],
765 parent: Nullable[ModelEntity] = None
766 ) -> None:
767 """
768 Initializes a selected waveform.
770 :param choices: List of all choices selecting this alternative.
771 :param waveform: List of all waveform elements, in the order they were written.
772 :param parent: The parent model entity of this entity.
773 """
774 super().__init__(parent)
775 WaveformMixin.__init__(self, waveform)
776 ChoicesMixin.__init__(self, choices)
778 def Visit(self, visitor: Visitor) -> None:
779 """
780 Walk this selected waveform, its waveform and its choices with a visitor.
782 :param visitor: The visitor receiving the events.
783 """
784 visitor.VisitSelectedWaveform(self)
785 self._VisitWaveform(visitor)
786 visitor.VisitWhen(self)
787 for index, choice in enumerate(self._choices):
788 if index > 0:
789 visitor.VisitBar(self)
791 choice.Visit(visitor)
793 visitor.DepartSelectedWaveform(self)
796@export
797class OthersSelectedWaveform(BaseCase, WaveformMixin):
798 """
799 Represents the ``others`` alternative of a selected signal assignment.
801 It supplies the waveform (:data:`Waveform`) for every choice not named explicitly.
803 .. admonition:: Example
805 .. code-block:: VHDL
807 with sel select s <= '1' when '0', '0' when others;
808 -- ^^^^^^^^^^^^^^^ <- the others alternative
809 """
811 def __init__(self, waveform: Iterable[WaveformElement], parent: Nullable[ModelEntity] = None) -> None:
812 """
813 Initializes an others selected waveform.
815 :param waveform: List of all waveform elements, in the order they were written.
816 :param parent: The parent model entity of this entity.
817 """
818 super().__init__(parent)
819 WaveformMixin.__init__(self, waveform)
821 def Visit(self, visitor: Visitor) -> None:
822 """
823 Walk this ``others`` selected waveform and its waveform with a visitor.
825 :param visitor: The visitor receiving the events.
826 """
827 visitor.VisitOthersSelectedWaveform(self)
828 self._VisitWaveform(visitor)
829 visitor.VisitWhen(self)
830 visitor.DepartOthersSelectedWaveform(self)
833@export
834class SelectedExpression(BaseCase, ExpressionMixin, ChoicesMixin):
835 """
836 Represents one alternative of a selected variable assignment.
838 Each alternative pairs an expression (:data:`Expression`) with the choices selecting it
839 (:data:`Choices`).
841 .. admonition:: Example
843 .. code-block:: VHDL
845 with sel select v := '1' when '0', '0' when others;
846 -- ^^^^^^^^^^^^ <- this alternative: Choices=['0'], Expression='1'
848 .. seealso::
850 * :class:`Selected waveform <pyVHDLModel.Common.SelectedWaveform>`
851 """
853 def __init__(
854 self,
855 choices: Iterable[BaseChoice],
856 expression: ExpressionUnion,
857 parent: Nullable[ModelEntity] = None
858 ) -> None:
859 """
860 Initializes a selected expression.
862 :param choices: List of all choices selecting this alternative.
863 :param expression: The value assigned for the matching choices.
864 :param parent: The parent model entity of this entity.
865 """
866 super().__init__(parent)
867 ExpressionMixin.__init__(self, expression)
868 ChoicesMixin.__init__(self, choices)
870 def Visit(self, visitor: Visitor) -> None:
871 """
872 Walk this selected expression, its value and its choices with a visitor.
874 :param visitor: The visitor receiving the events.
875 """
876 visitor.VisitSelectedExpression(self)
877 self._expression.Visit(visitor)
878 visitor.VisitWhen(self)
879 for index, choice in enumerate(self._choices):
880 if index > 0:
881 visitor.VisitBar(self)
883 choice.Visit(visitor)
885 visitor.DepartSelectedExpression(self)
888@export
889class OthersSelectedExpression(BaseCase, ExpressionMixin):
890 """
891 Represents the ``others`` alternative of a selected variable assignment.
893 It supplies the expression (:data:`Expression`) for every choice not named explicitly.
895 .. admonition:: Example
897 .. code-block:: VHDL
899 with sel select v := '1' when '0', '0' when others;
900 -- ^^^^^^^^^^^^^^^ <- the others alternative
901 """
903 def __init__(self, expression: ExpressionUnion, parent: Nullable[ModelEntity] = None) -> None:
904 """
905 Initializes an others selected expression.
907 :param expression: The value assigned for every unnamed choice.
908 :param parent: The parent model entity of this entity.
909 """
910 super().__init__(parent)
911 ExpressionMixin.__init__(self, expression)
913 def Visit(self, visitor: Visitor) -> None:
914 """
915 Walk this ``others`` selected expression and its value with a visitor.
917 :param visitor: The visitor receiving the events.
918 """
919 visitor.VisitOthersSelectedExpression(self)
920 self._expression.Visit(visitor)
921 visitor.VisitWhen(self)
922 visitor.DepartOthersSelectedExpression(self)
925@export
926class SelectedWaveformsMixin(metaclass=ExtendedType, mixin=True):
927 """
928 A mixin-class for all statements holding a list of :class:`SelectedWaveform`/
929 :class:`OthersSelectedWaveform` (both the concurrent and sequential forms of a selected signal
930 assignment).
932 .. seealso::
934 * :class:`Concurrent selected signal assignment <pyVHDLModel.Concurrent.ConcurrentSelectedSignalAssignment>`
935 * :class:`Sequential selected signal assignment <pyVHDLModel.Sequential.SequentialSelectedSignalAssignment>`
936 """
938 _selectedWaveforms: list[Union[SelectedWaveform, OthersSelectedWaveform]] #: All alternatives, in order.
940 def __init__(self, selectedWaveforms: Iterable[Union[SelectedWaveform, OthersSelectedWaveform]]) -> None:
941 """
942 Initializes selected waveforms.
944 :param selectedWaveforms: All alternatives, in order.
945 """
946 self._selectedWaveforms = selectedWaveformList = []
947 for selectedWaveform in selectedWaveforms:
948 selectedWaveformList.append(selectedWaveform)
949 selectedWaveform.Parent = self
951 def _VisitSelectedWaveforms(self, visitor: Visitor) -> None:
952 """
953 Walk the selected waveforms, separated by commas, with a visitor.
955 :param visitor: The visitor receiving the events.
956 """
957 for index, selectedWaveform in enumerate(self._selectedWaveforms):
958 if index > 0:
959 visitor.VisitComma(self)
961 selectedWaveform.Visit(visitor)
963 @readonly
964 def SelectedWaveforms(self) -> list[Union[SelectedWaveform, OthersSelectedWaveform]]:
965 """
966 Read-only property to access the selected waveforms (:attr:`_selectedWaveforms`).
968 :returns: List of selected waveforms.
969 """
970 return self._selectedWaveforms
973@export
974class SelectedExpressionsMixin(metaclass=ExtendedType, mixin=True):
975 """
976 A mixin-class for all statements holding a list of :class:`SelectedExpression`/
977 :class:`OthersSelectedExpression`.
979 .. seealso::
981 * :class:`Sequential selected variable assignment <pyVHDLModel.Sequential.SequentialSelectedVariableAssignment>`
982 """
984 _selectedExpressions: list[Union[SelectedExpression, OthersSelectedExpression]] #: All alternatives, in order.
986 def __init__(self, selectedExpressions: Iterable[Union[SelectedExpression, OthersSelectedExpression]]) -> None:
987 """
988 Initializes selected expressions.
990 :param selectedExpressions: All alternatives, in order.
991 """
992 self._selectedExpressions = selectedExpressionList = []
993 for selectedExpression in selectedExpressions:
994 selectedExpressionList.append(selectedExpression)
995 selectedExpression.Parent = self
997 def _VisitSelectedExpressions(self, visitor: Visitor) -> None:
998 """
999 Walk the selected expressions, separated by commas, with a visitor.
1001 :param visitor: The visitor receiving the events.
1002 """
1003 for index, selectedExpression in enumerate(self._selectedExpressions):
1004 if index > 0:
1005 visitor.VisitComma(self)
1007 selectedExpression.Visit(visitor)
1009 @readonly
1010 def SelectedExpressions(self) -> list[Union[SelectedExpression, OthersSelectedExpression]]:
1011 """
1012 Read-only property to access the selected expressions (:attr:`_selectedExpressions`).
1014 :returns: List of selected expressions.
1015 """
1016 return self._selectedExpressions