# ==================================================================================================================== #
# __ ___ _ ____ _ __ __ _ _ #
# _ __ _ \ \ / / | | | _ \| | | \/ | ___ __| | ___| | #
# | '_ \| | | \ \ / /| |_| | | | | | | |\/| |/ _ \ / _` |/ _ \ | #
# | |_) | |_| |\ V / | _ | |_| | |___| | | | (_) | (_| | __/ | #
# | .__/ \__, | \_/ |_| |_|____/|_____|_| |_|\___/ \__,_|\___|_| #
# |_| |___/ #
# ==================================================================================================================== #
# Authors: #
# Patrick Lehmann #
# #
# License: #
# ==================================================================================================================== #
# Copyright 2017-2026 Patrick Lehmann - Boetzingen, Germany #
# Copyright 2016-2017 Patrick Lehmann - Dresden, Germany #
# #
# Licensed under the Apache License, Version 2.0 (the "License"); #
# you may not use this file except in compliance with the License. #
# You may obtain a copy of the License at #
# #
# http://www.apache.org/licenses/LICENSE-2.0 #
# #
# Unless required by applicable law or agreed to in writing, software #
# distributed under the License is distributed on an "AS IS" BASIS, #
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
# See the License for the specific language governing permissions and #
# limitations under the License. #
# #
# SPDX-License-Identifier: Apache-2.0 #
# ==================================================================================================================== #
#
"""
This module contains parts of an abstract document language model for VHDL.
Common definitions and Mixins are used by many classes in the model as base-classes.
"""
from __future__ import annotations
from typing import TYPE_CHECKING, Iterable, Union, Optional as Nullable
from pyTooling.Common import getFullyQualifiedName, getTypeName
from pyTooling.Decorators import export, readonly
from pyTooling.MetaClasses import ExtendedType, abstractclass
from pyVHDLModel.Base import ModelEntity, LabeledEntityMixin, BaseCase, BaseChoice, WaveformElement, \
ConditionalMixin, ChoicesMixin
from pyVHDLModel.Exception import AlreadyInDesignError, NotAChildError, AssociationOrderError, \
DuplicateParameterAssociationError
from pyVHDLModel.Expression import BaseExpression, QualifiedExpression, FunctionCall, TypeConversion, Literal
from pyVHDLModel.Name import SimpleName
from pyVHDLModel.Symbol import Symbol, SignalSymbol, VariableSymbol
from pyVHDLModel.Association import ParameterAssociationItem
if TYPE_CHECKING:
from pyVHDLModel.Visitor import Visitor
ExpressionUnion = Union[
BaseExpression,
QualifiedExpression,
FunctionCall,
TypeConversion,
# ConstantOrSymbol, TODO: ObjectSymbol
Literal,
]
@export
class AllowBlackboxMixin(metaclass=ExtendedType, mixin=True, expects=("_parent",)):
"""
A mixin-class for language entities that may permit blackboxes.
The setting is inherited from the parent when not set locally (:data:`AllowBlackbox`). The host class provides the
reference to the parent ``_parent``.
.. seealso::
* :class:`Concurrent block statement <pyVHDLModel.Concurrent.ConcurrentBlockStatement>`
* :class:`Generate branch <pyVHDLModel.Concurrent.GenerateBranch>`
* :class:`Generate statement <pyVHDLModel.Concurrent.GenerateStatement>`
* :class:`Concurrent case <pyVHDLModel.Concurrent.ConcurrentCase>`
* :class:`Package <pyVHDLModel.DesignUnit.Package>`
* :class:`Entity <pyVHDLModel.DesignUnit.Entity>`
* :class:`Architecture <pyVHDLModel.DesignUnit.Architecture>`
* :class:`Component <pyVHDLModel.DesignUnit.Component>`
* :class:`Design <pyVHDLModel.Design>`
* :class:`Library <pyVHDLModel.Library>`
"""
_allowBlackbox: Nullable[bool] #: Allow blackboxes for components; ``None`` inherits the parent's setting.
def __init__(self, allowBlackbox: Nullable[bool] = None) -> None:
"""
Initializes a hierarchical model entity allow for blackboxes.
:param allowBlackbox: Allow blackboxes for components in language entity.
"""
self._allowBlackbox = allowBlackbox
@property
def AllowBlackbox(self) -> bool:
"""
Property to return whether a design supports blackboxes, inherited from the parent if not set locally
(:attr:`_allowBlackbox`).
.. rubric:: Algorithm
1. If allow blackbox property is locally set, return the local value,
2. Otherwise, return allow blackbox value from parent object.
:returns: ``True``, if blackboxes are allowed.
:raises VHDLModelException: If neither a local value is set nor a parent object is available to inherit the
value from.
"""
if self._allowBlackbox is not None:
return self._allowBlackbox
elif self._parent is None:
from pyVHDLModel.Exception import VHDLModelException
raise VHDLModelException(f"AllowBlackbox is not set on {self!r} and no parent is available to inherit it from.")
else:
return self._parent.AllowBlackbox
@AllowBlackbox.setter
def AllowBlackbox(self, value: Nullable[bool]) -> None:
self._allowBlackbox = value
@export
@abstractclass
class Statement(ModelEntity, LabeledEntityMixin):
"""
A ``Statement`` is a base-class for all statements.
.. seealso::
* :class:`Concurrent statement <pyVHDLModel.Concurrent.ConcurrentStatement>`
* :class:`Sequential statement <pyVHDLModel.Sequential.SequentialStatement>`
"""
def __init__(self, label: Nullable[str] = None, parent=None) -> None:
"""
Initializes a statement.
:param label: The label of a model entity.
:param parent: The parent model entity of this entity.
"""
super().__init__(parent)
LabeledEntityMixin.__init__(self, label)
@export
class ProcedureCallMixin(metaclass=ExtendedType, mixin=True):
"""
A mixin-class for statements calling a procedure.
The called procedure is available as :data:`Procedure`, its actual parameters as
:data:`ParameterAssociationItems`.
An association declares nothing: the mutators update neither a namespace nor a lookup table of the host class, so the
mixin expects nothing from its host.
.. seealso::
* :class:`Concurrent procedure call <pyVHDLModel.Concurrent.ConcurrentProcedureCall>`
* :class:`Sequential procedure call <pyVHDLModel.Sequential.SequentialProcedureCall>`
"""
# TODO: implement a ProcedureSymbol
_procedure: Symbol #: Reference to the called procedure.
_parameterAssociationItems: list[ParameterAssociationItem] #: List of all parameter associations of the call.
def __init__(self, procedureName: Symbol, parameterAssociationItems: Nullable[Iterable[ParameterAssociationItem]] = None) -> None:
"""
Initializes a procedure call.
:param procedureName: Reference to the called procedure.
:param parameterAssociationItems: List of all parameter associations of the call.
"""
self._procedure = procedureName
procedureName.Parent = self
# TODO: extract to mixin
self._parameterAssociationItems = parameterAssociationItemList = []
if parameterAssociationItems is not None:
for parameterMapping in parameterAssociationItems:
parameterAssociationItemList.append(parameterMapping)
parameterMapping.Parent = self
def _VisitProcedureCall(self, visitor: Visitor) -> None:
"""
Walk the called procedure and the actual parameter part, if there are parameters, with a visitor.
:param visitor: The visitor receiving the events.
"""
self._procedure.Visit(visitor)
if len(self._parameterAssociationItems) == 0:
return
visitor.VisitActualParameterPart(self)
for index, item in enumerate(self._parameterAssociationItems):
if index > 0:
visitor.VisitAssociationListSeparator(self)
item.Visit(visitor)
visitor.DepartActualParameterPart(self)
@readonly
def Procedure(self) -> Symbol:
"""
Read-only property to access the procedure (:attr:`_procedure`).
:returns: The procedure.
"""
return self._procedure
@readonly
def ParameterAssociationItems(self) -> list[ParameterAssociationItem]:
"""
Read-only property to access the parameter association items (:attr:`_parameterAssociationItems`).
:returns: List of parameter association items.
"""
return self._parameterAssociationItems
def AddParameterAssociationItem(self, parameterAssociationItem: ParameterAssociationItem) -> None:
"""
Add a parameter association at the end of the parameter association list.
The association's parent is set to this procedure call.
:param parameterAssociationItem: The parameter association to add.
:raises ValueError: If parameter 'parameterAssociationItem' is ``None``.
:raises TypeError: If parameter 'parameterAssociationItem' is not of type
:class:`~pyVHDLModel.Association.ParameterAssociationItem`.
:raises AlreadyInDesignError: If parameter 'parameterAssociationItem' already has a parent.
:raises DuplicateParameterAssociationError: If the association is named, and another named association of the
parameter association list has the same formal.
:raises AssociationOrderError: If the association is positional, and the parameter association list has
a named association.
.. seealso::
:meth:`InsertParameterAssociationItem`
|rarr| Insert a parameter association at a position of the parameter association list.
:meth:`RemoveParameterAssociationItem`
|rarr| Remove a parameter association from the parameter association list.
"""
self.InsertParameterAssociationItem(len(self._parameterAssociationItems), parameterAssociationItem)
def InsertParameterAssociationItem(self, index: int, parameterAssociationItem: ParameterAssociationItem) -> None:
"""
Insert a parameter association at a position of the parameter association list.
The association's parent is set to this procedure call.
A named association (``formal => actual``) can't repeat the formal of another named association of the parameter
association list. Only formals given as a simple name are compared: a partial association (``a(0) => x``) or a
conversion function (``to_integer(a) => x``) is accepted. All positional associations precede the named
associations.
:param index: Position, from ``0`` (first) to the number of parameter associations
(append). A negative index counts from the end, as for
:meth:`list.insert`: ``-1`` inserts before the last parameter
association.
:param parameterAssociationItem: The parameter association to insert.
:raises ValueError: If parameter 'index' is ``None``.
:raises TypeError: If parameter 'index' is not of type :class:`int`.
:raises ValueError: If parameter 'index' is out of range.
:raises ValueError: If parameter 'parameterAssociationItem' is ``None``.
:raises TypeError: If parameter 'parameterAssociationItem' is not of type
:class:`~pyVHDLModel.Association.ParameterAssociationItem`.
:raises AlreadyInDesignError: If parameter 'parameterAssociationItem' already has a parent.
:raises DuplicateParameterAssociationError: If the association is named, and another named association of the
parameter association list has the same formal.
:raises AssociationOrderError: If the association is positional, and a named association precedes the
position.
:raises AssociationOrderError: If the association is named, and a positional association follows the
position.
.. seealso::
:meth:`AddParameterAssociationItem`
|rarr| Add a parameter association at the end of the parameter association list.
:meth:`RemoveParameterAssociationItem`
|rarr| Remove a parameter association from the parameter association list.
"""
count = len(self._parameterAssociationItems)
if index is None:
raise ValueError("Parameter 'index' is None.")
elif not isinstance(index, int):
ex = TypeError("Parameter 'index' is not of type 'int'.")
ex.add_note(f"Got type '{getFullyQualifiedName(index)}'.")
raise ex
elif not (-count <= index <= count):
ex = ValueError(f"Parameter 'index' is out of range {-count}..{count}.")
ex.add_note(f"Got value '{index}'.")
raise ex
if parameterAssociationItem is None:
raise ValueError("Parameter 'parameterAssociationItem' is None.")
elif not isinstance(parameterAssociationItem, ParameterAssociationItem):
ex = TypeError("Parameter 'parameterAssociationItem' is not of type 'ParameterAssociationItem'.")
ex.add_note(f"Got type '{getFullyQualifiedName(parameterAssociationItem)}'.")
raise ex
elif parameterAssociationItem._parent is not None:
ex = AlreadyInDesignError(
f"{getTypeName(parameterAssociationItem)} already has a parent.",
element=parameterAssociationItem,
parent=parameterAssociationItem._parent
)
ex.add_note(f"Remove it from its {getTypeName(parameterAssociationItem._parent)} first.")
raise ex
formal = parameterAssociationItem._formal
if isinstance(formal, SimpleName):
normalizedFormal = formal._normalizedIdentifier
for existing in self._parameterAssociationItems:
if isinstance(existing._formal, SimpleName) and existing._formal._normalizedIdentifier == normalizedFormal:
ex = DuplicateParameterAssociationError(
f"{getTypeName(self)} already associates parameter '{formal._identifier}'.",
identifier=formal._identifier,
parent=self,
element=parameterAssociationItem,
existing=existing
)
ex.add_note(f"Existing association is '{existing!s}'.")
raise ex
position = index if index >= 0 else count + index
if formal is None:
for existing in self._parameterAssociationItems[:position]:
if existing._formal is not None:
ex = AssociationOrderError(
f"Positional parameter association can't follow named association '{existing!s}'.",
parent=self,
element=parameterAssociationItem,
existing=existing
)
ex.add_note("Insert it before the first named association, or name its formal.")
raise ex
else:
for existing in self._parameterAssociationItems[position:]:
if existing._formal is None:
ex = AssociationOrderError(
f"Named parameter association can't precede positional association '{existing!s}'.",
parent=self,
element=parameterAssociationItem,
existing=existing
)
ex.add_note("Insert it after the last positional association.")
raise ex
self._parameterAssociationItems.insert(index, parameterAssociationItem)
parameterAssociationItem.Parent = self
def RemoveParameterAssociationItem(self, parameterAssociationItem: ParameterAssociationItem) -> None:
"""
Remove a parameter association from the parameter association list.
The association is detached: its parent is cleared. The association itself is unchanged and can be added to another
procedure call, or inserted at another position.
:param parameterAssociationItem: The parameter association to remove.
:raises ValueError: If parameter 'parameterAssociationItem' is ``None``.
:raises TypeError: If parameter 'parameterAssociationItem' is not of type
:class:`~pyVHDLModel.Association.ParameterAssociationItem`.
:raises NotAChildError: If parameter 'parameterAssociationItem' is not a parameter association of this
procedure call.
.. seealso::
:meth:`AddParameterAssociationItem`
|rarr| Add a parameter association at the end of the parameter association list.
:meth:`InsertParameterAssociationItem`
|rarr| Insert a parameter association at a position of the parameter association list.
"""
if parameterAssociationItem is None:
raise ValueError("Parameter 'parameterAssociationItem' is None.")
elif not isinstance(parameterAssociationItem, ParameterAssociationItem):
ex = TypeError("Parameter 'parameterAssociationItem' is not of type 'ParameterAssociationItem'.")
ex.add_note(f"Got type '{getFullyQualifiedName(parameterAssociationItem)}'.")
raise ex
for index, item in enumerate(self._parameterAssociationItems):
if item is parameterAssociationItem:
break
else:
raise NotAChildError(
f"{getTypeName(parameterAssociationItem)} is not a parameter association of this {getTypeName(self)}.",
element=parameterAssociationItem,
parent=self
)
del self._parameterAssociationItems[index]
parameterAssociationItem._parent = None
@export
class AssignmentMixin(metaclass=ExtendedType, mixin=True):
"""
A mixin-class for all assignment statements.
.. seealso::
* :class:`Signal assignment mixin <pyVHDLModel.Common.SignalAssignmentMixin>`
* :class:`Variable assignment mixin <pyVHDLModel.Common.VariableAssignmentMixin>`
* :class:`Conditional variable assignment <pyVHDLModel.Sequential.SequentialConditionalVariableAssignment>`
* :class:`Sequential selected variable assignment <pyVHDLModel.Sequential.SequentialSelectedVariableAssignment>`
"""
_target: Symbol #: Reference to the assignment's destination.
def __init__(self, target: Symbol) -> None:
"""
Initializes an assignment.
:param target: Reference to the assignment's destination.
"""
self._target = target
target.Parent = self
@readonly
def Target(self) -> Symbol:
"""
Read-only property to access the target (:attr:`_target`).
:returns: The target.
"""
return self._target
@export
class SignalAssignmentMixin(AssignmentMixin, mixin=True):
"""
A mixin-class for all signal assignment statements.
.. seealso::
* :class:`Concurrent signal assignment <pyVHDLModel.Concurrent.ConcurrentSignalAssignment>`
* :class:`Sequential signal assignment <pyVHDLModel.Sequential.SequentialSignalAssignment>`
* :class:`Conditional signal assignment <pyVHDLModel.Sequential.SequentialConditionalSignalAssignment>`
* :class:`Sequential selected signal assignment <pyVHDLModel.Sequential.SequentialSelectedSignalAssignment>`
* :class:`Signal force assignment <pyVHDLModel.Sequential.SignalForceAssignment>`
* :class:`Signal release assignment <pyVHDLModel.Sequential.SignalReleaseAssignment>`
"""
@readonly
def Target(self) -> SignalSymbol:
"""
Read-only property to access the target (:attr:`_target`).
:returns: The target.
"""
return self._target
@export
class VariableAssignmentMixin(AssignmentMixin, mixin=True):
"""
A mixin-class for all variable assignment statements.
.. seealso::
* :class:`Sequential variable assignment <pyVHDLModel.Sequential.SequentialVariableAssignment>`
"""
# FIXME: move to sequential?
_expression: ExpressionUnion #: The assigned expression.
def __init__(self, target: VariableSymbol, expression: ExpressionUnion) -> None:
"""
Initializes a variable assignment.
:param target: Reference to the assignment's destination.
:param expression: The assigned expression.
"""
super().__init__(target)
self._expression = expression
expression.Parent = self
@readonly
def Target(self) -> VariableSymbol:
"""
Read-only property to access the target (:attr:`_target`).
:returns: The target.
"""
return self._target
@readonly
def Expression(self) -> ExpressionUnion:
"""
Read-only property to access the expression (:attr:`_expression`).
:returns: The expression.
"""
return self._expression
@export
class WaveformMixin(metaclass=ExtendedType, mixin=True):
"""
A mixin-class for all statements/entities holding a waveform (a list of :class:`WaveformElement`).
.. seealso::
* :class:`Conditional waveform <pyVHDLModel.Common.ConditionalWaveform>`
* :class:`Selected waveform <pyVHDLModel.Common.SelectedWaveform>`
* :class:`Others selected waveform <pyVHDLModel.Common.OthersSelectedWaveform>`
* :class:`Concurrent simple signal assignment <pyVHDLModel.Concurrent.ConcurrentSimpleSignalAssignment>`
* :class:`Sequential simple signal assignment <pyVHDLModel.Sequential.SequentialSimpleSignalAssignment>`
* :class:`Waveform element <pyVHDLModel.Base.WaveformElement>`
"""
_waveform: list[WaveformElement] #: List of all waveform elements, in the order they were written.
def __init__(self, waveform: Iterable[WaveformElement]) -> None:
"""
Initializes a waveform.
:param waveform: List of all waveform elements, in the order they were written.
"""
self._waveform = waveformList = []
for waveformElement in waveform:
waveformList.append(waveformElement)
waveformElement.Parent = self
def _VisitWaveform(self, visitor: Visitor) -> None:
"""
Walk the waveform elements, separated by commas, with a visitor.
:param visitor: The visitor receiving the events.
"""
for index, element in enumerate(self._waveform):
if index > 0:
visitor.VisitWaveformSeparator(self)
element.Visit(visitor)
@readonly
def Waveform(self) -> list[WaveformElement]:
"""
Read-only property to access the waveform (:attr:`_waveform`).
:returns: List of waveform.
"""
return self._waveform
@export
class ExpressionMixin(metaclass=ExtendedType, mixin=True):
"""
A mixin-class for all statements/entities holding a single expression.
.. seealso::
* :class:`Conditional expression <pyVHDLModel.Common.ConditionalExpression>`
* :class:`Selected expression <pyVHDLModel.Common.SelectedExpression>`
* :class:`Others selected expression <pyVHDLModel.Common.OthersSelectedExpression>`
* :class:`Concurrent selected signal assignment <pyVHDLModel.Concurrent.ConcurrentSelectedSignalAssignment>`
* :class:`Sequential selected variable assignment <pyVHDLModel.Sequential.SequentialSelectedVariableAssignment>`
* :class:`Sequential selected signal assignment <pyVHDLModel.Sequential.SequentialSelectedSignalAssignment>`
* :class:`Signal force assignment <pyVHDLModel.Sequential.SignalForceAssignment>`
"""
_expression: ExpressionUnion #: The expression held by this construct.
def __init__(self, expression: ExpressionUnion) -> None:
"""
Initializes an expression.
:param expression: The expression held by this construct.
"""
self._expression = expression
expression.Parent = self
@readonly
def Expression(self) -> ExpressionUnion:
"""
Read-only property to access the expression (:attr:`_expression`).
:returns: The expression.
"""
return self._expression
@export
class ConditionalWaveform(ModelEntity, WaveformMixin, ConditionalMixin):
"""
Represents one branch of a conditional signal assignment.
Each branch pairs a waveform (:data:`Waveform`) with a condition (:data:`Condition`). The final
branch has no ``when``, so its condition is ``None``.
.. admonition:: Example
.. code-block:: VHDL
s <= '1' when cond else '0';
-- ^^^^^^^^^^^^^ <- this branch: Waveform=['1'], Condition=cond
-- ^^^ <- final branch (no ``when``): Waveform=['0'], Condition=None
.. seealso::
* :class:`Waveform element <pyVHDLModel.Base.WaveformElement>`
* :class:`Conditional expression <pyVHDLModel.Common.ConditionalExpression>`
"""
def __init__(
self,
waveform: Iterable[WaveformElement],
condition: Nullable[ExpressionUnion] = None,
parent: Nullable[ModelEntity] = None
) -> None:
"""
Initializes a conditional waveform.
:param waveform: List of all waveform elements, in the order they were written.
:param condition: The condition selecting this alternative.
:param parent: The parent model entity of this entity.
"""
super().__init__(parent)
WaveformMixin.__init__(self, waveform)
ConditionalMixin.__init__(self, condition)
@export
class ConditionalExpression(ModelEntity, ExpressionMixin, ConditionalMixin):
"""
Represents one branch of a conditional variable assignment.
Each branch pairs an expression (:data:`Expression`) with a condition (:data:`Condition`). The
final branch has no ``when``, so its condition is ``None``.
.. admonition:: Example
.. code-block:: VHDL
v := '1' when cond else '0';
-- ^^^^^^^^^^^^^ <- this branch: Expression='1', Condition=cond
-- ^^^ <- final branch (no ``when``): Expression='0', Condition=None
.. seealso::
* :class:`Conditional waveform <pyVHDLModel.Common.ConditionalWaveform>`
"""
def __init__(
self,
expression: ExpressionUnion,
condition: Nullable[ExpressionUnion] = None,
parent: Nullable[ModelEntity] = None
) -> None:
"""
Initializes a conditional expression.
:param expression: The value assigned when the condition holds.
:param condition: The condition selecting this alternative.
:param parent: The parent model entity of this entity.
"""
super().__init__(parent)
ExpressionMixin.__init__(self, expression)
ConditionalMixin.__init__(self, condition)
[docs]
def Visit(self, visitor: Visitor) -> None:
"""
Walk this conditional expression, its value and its condition (if any) with a visitor.
:param visitor: The visitor receiving the events.
"""
visitor.VisitConditionalExpression(self)
self._expression.Visit(visitor)
if self._condition is not None:
visitor.VisitWhen(self)
self._condition.Visit(visitor)
visitor.DepartConditionalExpression(self)
@export
class ConditionalWaveformsMixin(metaclass=ExtendedType, mixin=True):
"""
A mixin-class for all statements holding a list of :class:`ConditionalWaveform` (both the
concurrent and sequential forms of a conditional signal assignment).
.. seealso::
* :class:`Conditional signal assignment <pyVHDLModel.Concurrent.ConcurrentConditionalSignalAssignment>`
* :class:`Conditional signal assignment <pyVHDLModel.Sequential.SequentialConditionalSignalAssignment>` """
_conditionalWaveforms: list[ConditionalWaveform] #: All alternatives, in order.
def __init__(self, conditionalWaveforms: Iterable[ConditionalWaveform]) -> None:
"""
Initializes conditional waveforms.
:param conditionalWaveforms: All alternatives, in order.
"""
self._conditionalWaveforms = conditionalWaveformList = []
for conditionalWaveform in conditionalWaveforms:
conditionalWaveformList.append(conditionalWaveform)
conditionalWaveform.Parent = self
def _VisitConditionalWaveforms(self, visitor: Visitor) -> None:
"""
Walk the conditional waveforms, separated by the reserved word ``else``, with a visitor.
:param visitor: The visitor receiving the events.
"""
for index, conditionalWaveform in enumerate(self._conditionalWaveforms):
if index > 0:
visitor.VisitElse(self)
conditionalWaveform.Visit(visitor)
@readonly
def ConditionalWaveforms(self) -> list[ConditionalWaveform]:
"""
Read-only property to access the conditional waveforms (:attr:`_conditionalWaveforms`).
:returns: List of conditional waveforms.
"""
return self._conditionalWaveforms
@export
class SelectedWaveform(BaseCase, WaveformMixin, ChoicesMixin):
"""
Represents one alternative of a selected signal assignment.
Each alternative pairs a waveform (:data:`Waveform`) with the choices selecting it
(:data:`Choices`).
.. admonition:: Example
.. code-block:: VHDL
with sel select s <= '1' when '0', '0' when others;
-- ^^^^^^^^^^^^ <- this alternative: Choices=['0'], Waveform=['1']
.. seealso::
* :class:`Waveform element <pyVHDLModel.Base.WaveformElement>`
* :class:`Selected expression <pyVHDLModel.Common.SelectedExpression>`
"""
def __init__(
self,
choices: Iterable[BaseChoice],
waveform: Iterable[WaveformElement],
parent: Nullable[ModelEntity] = None
) -> None:
"""
Initializes a selected waveform.
:param choices: List of all choices selecting this alternative.
:param waveform: List of all waveform elements, in the order they were written.
:param parent: The parent model entity of this entity.
"""
super().__init__(parent)
WaveformMixin.__init__(self, waveform)
ChoicesMixin.__init__(self, choices)
@export
class OthersSelectedWaveform(BaseCase, WaveformMixin):
"""
Represents the ``others`` alternative of a selected signal assignment.
It supplies the waveform (:data:`Waveform`) for every choice not named explicitly.
.. admonition:: Example
.. code-block:: VHDL
with sel select s <= '1' when '0', '0' when others;
-- ^^^^^^^^^^^^^^^ <- the others alternative
"""
def __init__(self, waveform: Iterable[WaveformElement], parent: Nullable[ModelEntity] = None) -> None:
"""
Initializes an others selected waveform.
:param waveform: List of all waveform elements, in the order they were written.
:param parent: The parent model entity of this entity.
"""
super().__init__(parent)
WaveformMixin.__init__(self, waveform)
@export
class SelectedExpression(BaseCase, ExpressionMixin, ChoicesMixin):
"""
Represents one alternative of a selected variable assignment.
Each alternative pairs an expression (:data:`Expression`) with the choices selecting it
(:data:`Choices`).
.. admonition:: Example
.. code-block:: VHDL
with sel select v := '1' when '0', '0' when others;
-- ^^^^^^^^^^^^ <- this alternative: Choices=['0'], Expression='1'
.. seealso::
* :class:`Selected waveform <pyVHDLModel.Common.SelectedWaveform>`
"""
def __init__(
self,
choices: Iterable[BaseChoice],
expression: ExpressionUnion,
parent: Nullable[ModelEntity] = None
) -> None:
"""
Initializes a selected expression.
:param choices: List of all choices selecting this alternative.
:param expression: The value assigned for the matching choices.
:param parent: The parent model entity of this entity.
"""
super().__init__(parent)
ExpressionMixin.__init__(self, expression)
ChoicesMixin.__init__(self, choices)
[docs]
def Visit(self, visitor: Visitor) -> None:
"""
Walk this selected expression, its value and its choices with a visitor.
:param visitor: The visitor receiving the events.
"""
visitor.VisitSelectedExpression(self)
self._expression.Visit(visitor)
visitor.VisitWhen(self)
for index, choice in enumerate(self._choices):
if index > 0:
visitor.VisitBar(self)
choice.Visit(visitor)
visitor.DepartSelectedExpression(self)
@export
class OthersSelectedExpression(BaseCase, ExpressionMixin):
"""
Represents the ``others`` alternative of a selected variable assignment.
It supplies the expression (:data:`Expression`) for every choice not named explicitly.
.. admonition:: Example
.. code-block:: VHDL
with sel select v := '1' when '0', '0' when others;
-- ^^^^^^^^^^^^^^^ <- the others alternative
"""
def __init__(self, expression: ExpressionUnion, parent: Nullable[ModelEntity] = None) -> None:
"""
Initializes an others selected expression.
:param expression: The value assigned for every unnamed choice.
:param parent: The parent model entity of this entity.
"""
super().__init__(parent)
ExpressionMixin.__init__(self, expression)
@export
class SelectedWaveformsMixin(metaclass=ExtendedType, mixin=True):
"""
A mixin-class for all statements holding a list of :class:`SelectedWaveform`/
:class:`OthersSelectedWaveform` (both the concurrent and sequential forms of a selected signal
assignment).
.. seealso::
* :class:`Concurrent selected signal assignment <pyVHDLModel.Concurrent.ConcurrentSelectedSignalAssignment>`
* :class:`Sequential selected signal assignment <pyVHDLModel.Sequential.SequentialSelectedSignalAssignment>`
"""
_selectedWaveforms: list[Union[SelectedWaveform, OthersSelectedWaveform]] #: All alternatives, in order.
def __init__(self, selectedWaveforms: Iterable[Union[SelectedWaveform, OthersSelectedWaveform]]) -> None:
"""
Initializes selected waveforms.
:param selectedWaveforms: All alternatives, in order.
"""
self._selectedWaveforms = selectedWaveformList = []
for selectedWaveform in selectedWaveforms:
selectedWaveformList.append(selectedWaveform)
selectedWaveform.Parent = self
def _VisitSelectedWaveforms(self, visitor: Visitor) -> None:
"""
Walk the selected waveforms, separated by commas, with a visitor.
:param visitor: The visitor receiving the events.
"""
for index, selectedWaveform in enumerate(self._selectedWaveforms):
if index > 0:
visitor.VisitSelectedAlternativeSeparator(self)
selectedWaveform.Visit(visitor)
@readonly
def SelectedWaveforms(self) -> list[Union[SelectedWaveform, OthersSelectedWaveform]]:
"""
Read-only property to access the selected waveforms (:attr:`_selectedWaveforms`).
:returns: List of selected waveforms.
"""
return self._selectedWaveforms
@export
class SelectedExpressionsMixin(metaclass=ExtendedType, mixin=True):
"""
A mixin-class for all statements holding a list of :class:`SelectedExpression`/
:class:`OthersSelectedExpression`.
.. seealso::
* :class:`Sequential selected variable assignment <pyVHDLModel.Sequential.SequentialSelectedVariableAssignment>`
"""
_selectedExpressions: list[Union[SelectedExpression, OthersSelectedExpression]] #: All alternatives, in order.
def __init__(self, selectedExpressions: Iterable[Union[SelectedExpression, OthersSelectedExpression]]) -> None:
"""
Initializes selected expressions.
:param selectedExpressions: All alternatives, in order.
"""
self._selectedExpressions = selectedExpressionList = []
for selectedExpression in selectedExpressions:
selectedExpressionList.append(selectedExpression)
selectedExpression.Parent = self
def _VisitSelectedExpressions(self, visitor: Visitor) -> None:
"""
Walk the selected expressions, separated by commas, with a visitor.
:param visitor: The visitor receiving the events.
"""
for index, selectedExpression in enumerate(self._selectedExpressions):
if index > 0:
visitor.VisitSelectedAlternativeSeparator(self)
selectedExpression.Visit(visitor)
@readonly
def SelectedExpressions(self) -> list[Union[SelectedExpression, OthersSelectedExpression]]:
"""
Read-only property to access the selected expressions (:attr:`_selectedExpressions`).
:returns: List of selected expressions.
"""
return self._selectedExpressions