Implement RFC 35: Add ShapeLike
, ValueLike
.
This commit is contained in:
parent
422ba9ea51
commit
e9545efb22
|
@ -4,7 +4,7 @@ import warnings
|
||||||
import functools
|
import functools
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from collections.abc import Iterable, MutableMapping, MutableSet, MutableSequence
|
from collections.abc import Iterable, MutableMapping, MutableSet, MutableSequence
|
||||||
from enum import Enum
|
from enum import Enum, EnumMeta
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
|
|
||||||
from ._repr import *
|
from ._repr import *
|
||||||
|
@ -15,11 +15,11 @@ from .._unused import *
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"Shape", "signed", "unsigned", "ShapeCastable",
|
"Shape", "signed", "unsigned", "ShapeCastable", "ShapeLike",
|
||||||
"Value", "Const", "C", "AnyConst", "AnySeq", "Operator", "Mux", "Part", "Slice", "Cat", "Repl",
|
"Value", "Const", "C", "AnyConst", "AnySeq", "Operator", "Mux", "Part", "Slice", "Cat", "Repl",
|
||||||
"Array", "ArrayProxy",
|
"Array", "ArrayProxy",
|
||||||
"Signal", "ClockSignal", "ResetSignal",
|
"Signal", "ClockSignal", "ResetSignal",
|
||||||
"ValueCastable",
|
"ValueCastable", "ValueLike",
|
||||||
"Sample", "Past", "Stable", "Rose", "Fell", "Initial",
|
"Sample", "Past", "Stable", "Rose", "Fell", "Initial",
|
||||||
"Statement", "Switch",
|
"Statement", "Switch",
|
||||||
"Property", "Assign", "Assert", "Assume", "Cover",
|
"Property", "Assign", "Assert", "Assume", "Cover",
|
||||||
|
@ -150,6 +150,52 @@ class Shape:
|
||||||
self.width == other.width and self.signed == other.signed)
|
self.width == other.width and self.signed == other.signed)
|
||||||
|
|
||||||
|
|
||||||
|
class _ShapeLikeMeta(type):
|
||||||
|
def __subclasscheck__(cls, subclass):
|
||||||
|
return issubclass(subclass, (Shape, ShapeCastable, int, range, EnumMeta)) or subclass is ShapeLike
|
||||||
|
|
||||||
|
def __instancecheck__(cls, instance):
|
||||||
|
if isinstance(instance, (Shape, ShapeCastable, range)):
|
||||||
|
return True
|
||||||
|
if isinstance(instance, int):
|
||||||
|
return instance >= 0
|
||||||
|
if isinstance(instance, EnumMeta):
|
||||||
|
for member in instance:
|
||||||
|
if not isinstance(member.value, ValueLike):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
@final
|
||||||
|
class ShapeLike(metaclass=_ShapeLikeMeta):
|
||||||
|
"""An abstract class representing all objects that can be cast to a :class:`Shape`.
|
||||||
|
|
||||||
|
``issubclass(cls, ShapeLike)`` returns ``True`` for:
|
||||||
|
|
||||||
|
- :class:`Shape`
|
||||||
|
- :class:`ShapeCastable` and its subclasses
|
||||||
|
- ``int`` and its subclasses
|
||||||
|
- ``range`` and its subclasses
|
||||||
|
- :class:`enum.EnumMeta` and its subclasses
|
||||||
|
- :class:`ShapeLike` itself
|
||||||
|
|
||||||
|
``isinstance(obj, ShapeLike)`` returns ``True`` for:
|
||||||
|
|
||||||
|
- :class:`Shape` instances
|
||||||
|
- :class:`ShapeCastable` instances
|
||||||
|
- non-negative ``int`` values
|
||||||
|
- ``range`` instances
|
||||||
|
- :class:`enum.Enum` subclasses where all values are :ref:`value-like <lang-valuelike>`
|
||||||
|
|
||||||
|
This class is only usable for the above checks — no instances and no (non-virtual)
|
||||||
|
subclasses can be created.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __new__(cls, *args, **kwargs):
|
||||||
|
raise TypeError("ShapeLike is an abstract class and cannot be constructed")
|
||||||
|
|
||||||
|
|
||||||
def unsigned(width):
|
def unsigned(width):
|
||||||
"""Shorthand for ``Shape(width, signed=False)``."""
|
"""Shorthand for ``Shape(width, signed=False)``."""
|
||||||
return Shape(width, signed=False)
|
return Shape(width, signed=False)
|
||||||
|
@ -1479,6 +1525,40 @@ class ValueCastable:
|
||||||
return wrapper_memoized
|
return wrapper_memoized
|
||||||
|
|
||||||
|
|
||||||
|
class _ValueLikeMeta(type):
|
||||||
|
"""An abstract class representing all objects that can be cast to a :class:`Value`.
|
||||||
|
|
||||||
|
``issubclass(cls, ValueLike)`` returns ``True`` for:
|
||||||
|
|
||||||
|
- :class:`Value`
|
||||||
|
- :class:`ValueCastable` and its subclasses
|
||||||
|
- ``int`` and its subclasses
|
||||||
|
- :class:`enum.Enum` subclasses where all values are :ref:`value-like <lang-valuelike>`
|
||||||
|
- :class:`ValueLike` itself
|
||||||
|
|
||||||
|
``isinstance(obj, ValueLike)`` returns the same value as ``issubclass(type(obj), ValueLike)``.
|
||||||
|
|
||||||
|
This class is only usable for the above checks — no instances and no (non-virtual)
|
||||||
|
subclasses can be created.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __subclasscheck__(cls, subclass):
|
||||||
|
if issubclass(subclass, (Value, ValueCastable, int)) or subclass is ValueLike:
|
||||||
|
return True
|
||||||
|
if issubclass(subclass, Enum):
|
||||||
|
return isinstance(subclass, ShapeLike)
|
||||||
|
return False
|
||||||
|
|
||||||
|
def __instancecheck__(cls, instance):
|
||||||
|
return issubclass(type(instance), cls)
|
||||||
|
|
||||||
|
|
||||||
|
@final
|
||||||
|
class ValueLike(metaclass=_ValueLikeMeta):
|
||||||
|
def __new__(cls, *args, **kwargs):
|
||||||
|
raise TypeError("ValueLike is an abstract class and cannot be constructed")
|
||||||
|
|
||||||
|
|
||||||
# TODO(amaranth-0.5): remove
|
# TODO(amaranth-0.5): remove
|
||||||
@final
|
@final
|
||||||
class Sample(Value):
|
class Sample(Value):
|
||||||
|
|
|
@ -26,7 +26,7 @@ class Field:
|
||||||
|
|
||||||
Attributes
|
Attributes
|
||||||
----------
|
----------
|
||||||
shape : :ref:`shape-castable <lang-shapecasting>`
|
shape : :ref:`shape-like <lang-shapelike>`
|
||||||
Shape of the field. When initialized or assigned, the object is stored as-is.
|
Shape of the field. When initialized or assigned, the object is stored as-is.
|
||||||
offset : :class:`int`, >=0
|
offset : :class:`int`, >=0
|
||||||
Index of the least significant bit of the field.
|
Index of the least significant bit of the field.
|
||||||
|
@ -56,7 +56,7 @@ class Field:
|
||||||
"""Width of the field.
|
"""Width of the field.
|
||||||
|
|
||||||
This property should be used over ``self.shape.width`` because ``self.shape`` can be
|
This property should be used over ``self.shape.width`` because ``self.shape`` can be
|
||||||
an arbitrary :ref:`shape-castable <lang-shapecasting>` object, which may not have
|
an arbitrary :ref:`shape-like <lang-shapelike>` object, which may not have
|
||||||
a ``width`` property.
|
a ``width`` property.
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
|
@ -82,7 +82,7 @@ class Field:
|
||||||
class Layout(ShapeCastable, metaclass=ABCMeta):
|
class Layout(ShapeCastable, metaclass=ABCMeta):
|
||||||
"""Description of a data layout.
|
"""Description of a data layout.
|
||||||
|
|
||||||
The :ref:`shape-castable <lang-shapecasting>` :class:`Layout` interface associates keys
|
The :ref:`shape-like <lang-shapelike>` :class:`Layout` interface associates keys
|
||||||
(string names or integer indexes) with fields, giving identifiers to spans of bits in
|
(string names or integer indexes) with fields, giving identifiers to spans of bits in
|
||||||
an Amaranth value.
|
an Amaranth value.
|
||||||
|
|
||||||
|
@ -96,7 +96,7 @@ class Layout(ShapeCastable, metaclass=ABCMeta):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def cast(obj):
|
def cast(obj):
|
||||||
"""Cast a :ref:`shape-castable <lang-shapecasting>` object to a layout.
|
"""Cast a :ref:`shape-like <lang-shapelike>` object to a layout.
|
||||||
|
|
||||||
This method performs a subset of the operations done by :meth:`Shape.cast`; it will
|
This method performs a subset of the operations done by :meth:`Shape.cast`; it will
|
||||||
recursively call ``.as_shape()``, but only until a layout is returned.
|
recursively call ``.as_shape()``, but only until a layout is returned.
|
||||||
|
@ -279,7 +279,7 @@ class StructLayout(Layout):
|
||||||
|
|
||||||
Attributes
|
Attributes
|
||||||
----------
|
----------
|
||||||
members : mapping of :class:`str` to :ref:`shape-castable <lang-shapecasting>`
|
members : mapping of :class:`str` to :ref:`shape-like <lang-shapelike>`
|
||||||
Dictionary of structure members.
|
Dictionary of structure members.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -350,7 +350,7 @@ class UnionLayout(Layout):
|
||||||
|
|
||||||
Attributes
|
Attributes
|
||||||
----------
|
----------
|
||||||
members : mapping of :class:`str` to :ref:`shape-castable <lang-shapecasting>`
|
members : mapping of :class:`str` to :ref:`shape-like <lang-shapelike>`
|
||||||
Dictionary of union members.
|
Dictionary of union members.
|
||||||
"""
|
"""
|
||||||
def __init__(self, members):
|
def __init__(self, members):
|
||||||
|
@ -425,7 +425,7 @@ class ArrayLayout(Layout):
|
||||||
|
|
||||||
Attributes
|
Attributes
|
||||||
----------
|
----------
|
||||||
elem_shape : :ref:`shape-castable <lang-shapecasting>`
|
elem_shape : :ref:`shape-like <lang-shapelike>`
|
||||||
Shape of an individual element.
|
Shape of an individual element.
|
||||||
length : :class:`int`
|
length : :class:`int`
|
||||||
Amount of elements.
|
Amount of elements.
|
||||||
|
@ -567,7 +567,7 @@ class FlexibleLayout(Layout):
|
||||||
class View(ValueCastable):
|
class View(ValueCastable):
|
||||||
"""A value viewed through the lens of a layout.
|
"""A value viewed through the lens of a layout.
|
||||||
|
|
||||||
The :ref:`value-castable <lang-valuecasting>` class :class:`View` provides access to the fields
|
The :ref:`value-like <lang-valuelike>` class :class:`View` provides access to the fields
|
||||||
of an underlying Amaranth value via the names or indexes defined in the provided layout.
|
of an underlying Amaranth value via the names or indexes defined in the provided layout.
|
||||||
|
|
||||||
Creating a view
|
Creating a view
|
||||||
|
@ -583,7 +583,7 @@ class View(ValueCastable):
|
||||||
a value-castable object. If the shape of the field is a :class:`Layout`, it will be
|
a value-castable object. If the shape of the field is a :class:`Layout`, it will be
|
||||||
a :class:`View`; if it is a class deriving from :class:`Struct` or :class:`Union`, it
|
a :class:`View`; if it is a class deriving from :class:`Struct` or :class:`Union`, it
|
||||||
will be an instance of that data class; if it is another
|
will be an instance of that data class; if it is another
|
||||||
:ref:`shape-castable <lang-shapecasting>` object implementing ``__call__``, it will be
|
:ref:`shape-like <lang-shapelike>` object implementing ``__call__``, it will be
|
||||||
the result of calling that method.
|
the result of calling that method.
|
||||||
|
|
||||||
Slicing a view whose layout is an :class:`ArrayLayout` can be done with an index that is
|
Slicing a view whose layout is an :class:`ArrayLayout` can be done with an index that is
|
||||||
|
@ -859,7 +859,7 @@ class Struct(View, metaclass=_AggregateMeta):
|
||||||
to describe the structure layout and reset values for the fields using Python
|
to describe the structure layout and reset values for the fields using Python
|
||||||
:term:`variable annotations <python:variable annotation>`.
|
:term:`variable annotations <python:variable annotation>`.
|
||||||
|
|
||||||
Any annotations containing :ref:`shape-castable <lang-shapecasting>` objects are used,
|
Any annotations containing :ref:`shape-like <lang-shapelike>` objects are used,
|
||||||
in the order in which they appear in the source code, to construct a :class:`StructLayout`.
|
in the order in which they appear in the source code, to construct a :class:`StructLayout`.
|
||||||
The values assigned to such annotations are used to populate the reset value of the signal
|
The values assigned to such annotations are used to populate the reset value of the signal
|
||||||
created by the view. Any other annotations are kept as-is.
|
created by the view. Any other annotations are kept as-is.
|
||||||
|
|
|
@ -19,13 +19,13 @@ class EnumMeta(ShapeCastable, py_enum.EnumMeta):
|
||||||
protocol.
|
protocol.
|
||||||
|
|
||||||
This metaclass provides the :meth:`as_shape` method, making its instances
|
This metaclass provides the :meth:`as_shape` method, making its instances
|
||||||
:ref:`shape-castable <lang-shapecasting>`, and accepts a ``shape=`` keyword argument
|
:ref:`shape-like <lang-shapelike>`, and accepts a ``shape=`` keyword argument
|
||||||
to specify a shape explicitly. Other than this, it acts the same as the standard
|
to specify a shape explicitly. Other than this, it acts the same as the standard
|
||||||
:class:`enum.EnumMeta` class; if the ``shape=`` argument is not specified and
|
:class:`enum.EnumMeta` class; if the ``shape=`` argument is not specified and
|
||||||
:meth:`as_shape` is never called, it places no restrictions on the enumeration class
|
:meth:`as_shape` is never called, it places no restrictions on the enumeration class
|
||||||
or the values of its members.
|
or the values of its members.
|
||||||
|
|
||||||
When a :ref:`value-castable <lang-valuecasting>` is cast to an enum type that is an instance
|
When a :ref:`value-like <lang-valuelike>` is cast to an enum type that is an instance
|
||||||
of this metaclass, it can be automatically wrapped in a view class. A custom view class
|
of this metaclass, it can be automatically wrapped in a view class. A custom view class
|
||||||
can be specified by passing the ``view_class=`` keyword argument when creating the enum class.
|
can be specified by passing the ``view_class=`` keyword argument when creating the enum class.
|
||||||
"""
|
"""
|
||||||
|
@ -139,7 +139,7 @@ class EnumMeta(ShapeCastable, py_enum.EnumMeta):
|
||||||
When given an integer constant, it returns the corresponding enum value, like a standard
|
When given an integer constant, it returns the corresponding enum value, like a standard
|
||||||
Python enumeration.
|
Python enumeration.
|
||||||
|
|
||||||
When given a :ref:`value-castable <lang-valuecasting>`, it is cast to a value, then wrapped
|
When given a :ref:`value-like <lang-valuelike>`, it is cast to a value, then wrapped
|
||||||
in the ``view_class`` specified for this enum type (:class:`EnumView` for :class:`Enum`,
|
in the ``view_class`` specified for this enum type (:class:`EnumView` for :class:`Enum`,
|
||||||
:class:`FlagView` for :class:`Flag`, or a custom user-defined class). If the type has no
|
:class:`FlagView` for :class:`Flag`, or a custom user-defined class). If the type has no
|
||||||
``view_class`` (like :class:`IntEnum` or :class:`IntFlag`), a plain
|
``view_class`` (like :class:`IntEnum` or :class:`IntFlag`), a plain
|
||||||
|
@ -214,7 +214,7 @@ class EnumView(ValueCastable):
|
||||||
|
|
||||||
def __init__(self, enum, target):
|
def __init__(self, enum, target):
|
||||||
"""Constructs a view with the given enum type and target
|
"""Constructs a view with the given enum type and target
|
||||||
(a :ref:`value-castable <lang-valuecasting>`).
|
(a :ref:`value-like <lang-valuelike>`).
|
||||||
"""
|
"""
|
||||||
if not isinstance(enum, EnumMeta) or not hasattr(enum, "_amaranth_shape_"):
|
if not isinstance(enum, EnumMeta) or not hasattr(enum, "_amaranth_shape_"):
|
||||||
raise TypeError(f"EnumView type must be an enum with shape, not {enum!r}")
|
raise TypeError(f"EnumView type must be an enum with shape, not {enum!r}")
|
||||||
|
@ -312,7 +312,7 @@ class FlagView(EnumView):
|
||||||
values of the same enum type."""
|
values of the same enum type."""
|
||||||
|
|
||||||
def __invert__(self):
|
def __invert__(self):
|
||||||
"""Inverts all flags in this value and returns another :ref:`FlagView`.
|
"""Inverts all flags in this value and returns another :class:`FlagView`.
|
||||||
|
|
||||||
Note that this is not equivalent to applying bitwise negation to the underlying value:
|
Note that this is not equivalent to applying bitwise negation to the underlying value:
|
||||||
just like the Python :class:`enum.Flag` class, only bits corresponding to flags actually
|
just like the Python :class:`enum.Flag` class, only bits corresponding to flags actually
|
||||||
|
|
|
@ -121,14 +121,14 @@ The shape of the constant can be specified explicitly, in which case the number'
|
||||||
0
|
0
|
||||||
|
|
||||||
|
|
||||||
.. _lang-shapecasting:
|
.. _lang-shapelike:
|
||||||
|
|
||||||
Shape casting
|
Shape casting
|
||||||
=============
|
=============
|
||||||
|
|
||||||
Shapes can be *cast* from other objects, which are called *shape-castable*. Casting is a convenient way to specify a shape indirectly, for example, by a range of numbers representable by values with that shape.
|
Shapes can be *cast* from other objects, which are called *shape-like*. Casting is a convenient way to specify a shape indirectly, for example, by a range of numbers representable by values with that shape.
|
||||||
|
|
||||||
Casting to a shape can be done explicitly with ``Shape.cast``, but is usually implicit, since shape-castable objects are accepted anywhere shapes are.
|
Casting to a shape can be done explicitly with ``Shape.cast``, but is usually implicit, since shape-like objects are accepted anywhere shapes are.
|
||||||
|
|
||||||
|
|
||||||
.. _lang-shapeint:
|
.. _lang-shapeint:
|
||||||
|
@ -244,16 +244,16 @@ The :mod:`amaranth.lib.enum` module extends the standard enumerations such that
|
||||||
The enumeration does not have to subclass :class:`enum.IntEnum` or have :class:`int` as one of its base classes; it only needs to have integers as values of every member. Using enumerations based on :class:`enum.Enum` rather than :class:`enum.IntEnum` prevents unwanted implicit conversion of enum members to integers.
|
The enumeration does not have to subclass :class:`enum.IntEnum` or have :class:`int` as one of its base classes; it only needs to have integers as values of every member. Using enumerations based on :class:`enum.Enum` rather than :class:`enum.IntEnum` prevents unwanted implicit conversion of enum members to integers.
|
||||||
|
|
||||||
|
|
||||||
.. _lang-valuecasting:
|
.. _lang-valuelike:
|
||||||
|
|
||||||
Value casting
|
Value casting
|
||||||
=============
|
=============
|
||||||
|
|
||||||
Like shapes, values may be *cast* from other objects, which are called *value-castable*. Casting to values allows objects that are not provided by Amaranth, such as integers or enumeration members, to be used in Amaranth expressions directly.
|
Like shapes, values may be *cast* from other objects, which are called *value-like*. Casting to values allows objects that are not provided by Amaranth, such as integers or enumeration members, to be used in Amaranth expressions directly.
|
||||||
|
|
||||||
.. TODO: link to ValueCastable
|
.. TODO: link to ValueCastable
|
||||||
|
|
||||||
Casting to a value can be done explicitly with ``Value.cast``, but is usually implicit, since value-castable objects are accepted anywhere values are.
|
Casting to a value can be done explicitly with ``Value.cast``, but is usually implicit, since value-like objects are accepted anywhere values are.
|
||||||
|
|
||||||
|
|
||||||
Values from integers
|
Values from integers
|
||||||
|
@ -343,7 +343,7 @@ A *signal* is a value representing a (potentially) varying number. Signals can b
|
||||||
Signal shapes
|
Signal shapes
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
A signal can be created with an explicitly specified shape (any :ref:`shape-castable <lang-shapecasting>` object); if omitted, the shape defaults to ``unsigned(1)``. Although rarely useful, 0-bit signals are permitted.
|
A signal can be created with an explicitly specified shape (any :ref:`shape-like <lang-shapelike>` object); if omitted, the shape defaults to ``unsigned(1)``. Although rarely useful, 0-bit signals are permitted.
|
||||||
|
|
||||||
.. doctest::
|
.. doctest::
|
||||||
|
|
||||||
|
@ -444,7 +444,7 @@ Amaranth provides aggregate data structures in the standard library module :mod:
|
||||||
Operators
|
Operators
|
||||||
=========
|
=========
|
||||||
|
|
||||||
To describe computations, Amaranth values can be combined with each other or with :ref:`value-castable <lang-valuecasting>` objects using a rich array of arithmetic, bitwise, logical, bit sequence, and other *operators* to form *expressions*, which are themselves values.
|
To describe computations, Amaranth values can be combined with each other or with :ref:`value-like <lang-valuelike>` objects using a rich array of arithmetic, bitwise, logical, bit sequence, and other *operators* to form *expressions*, which are themselves values.
|
||||||
|
|
||||||
|
|
||||||
.. _lang-abstractexpr:
|
.. _lang-abstractexpr:
|
||||||
|
|
|
@ -61,7 +61,7 @@ While this implementation works, it is repetitive, error-prone, hard to read, an
|
||||||
|
|
||||||
m.d.comb += o_gray.eq((i_color.red + i_color.green + i_color.blue) << 1)
|
m.d.comb += o_gray.eq((i_color.red + i_color.green + i_color.blue) << 1)
|
||||||
|
|
||||||
The :class:`View` is :ref:`value-castable <lang-valuecasting>` and can be used anywhere a plain value can be used. For example, it can be assigned to in the usual way:
|
The :class:`View` is :ref:`value-like <lang-valuelike>` and can be used anywhere a plain value can be used. For example, it can be assigned to in the usual way:
|
||||||
|
|
||||||
.. testcode::
|
.. testcode::
|
||||||
|
|
||||||
|
@ -135,7 +135,7 @@ In case the data has related operations or transformations, :class:`View` can be
|
||||||
def brightness(self):
|
def brightness(self):
|
||||||
return (self.red + self.green + self.blue)[-8:]
|
return (self.red + self.green + self.blue)[-8:]
|
||||||
|
|
||||||
Here, the ``RGBLayout`` class itself is :ref:`shape-castable <lang-shapecasting>` and can be used anywhere a shape is accepted. When a :class:`Signal` is constructed with this layout, the returned value is wrapped in an ``RGBView``:
|
Here, the ``RGBLayout`` class itself is :ref:`shape-like <lang-shapelike>` and can be used anywhere a shape is accepted. When a :class:`Signal` is constructed with this layout, the returned value is wrapped in an ``RGBView``:
|
||||||
|
|
||||||
.. doctest::
|
.. doctest::
|
||||||
|
|
||||||
|
|
|
@ -59,7 +59,7 @@ The ``shape=`` argument is optional. If not specified, classes from this module
|
||||||
|
|
||||||
In this way, this module is a drop-in replacement for the standard :mod:`enum` module, and in an Amaranth project, all ``import enum`` statements may be replaced with ``from amaranth.lib import enum``.
|
In this way, this module is a drop-in replacement for the standard :mod:`enum` module, and in an Amaranth project, all ``import enum`` statements may be replaced with ``from amaranth.lib import enum``.
|
||||||
|
|
||||||
Signals with :class:`Enum` or :class:`Flag` based shape are automatically wrapped in the :class:`EnumView` or :class:`FlagView` value-castable wrappers, which ensure type safety. Any :ref:`value-castable <lang-valuecasting>` can also be explicitly wrapped in a view class by casting it to the enum type:
|
Signals with :class:`Enum` or :class:`Flag` based shape are automatically wrapped in the :class:`EnumView` or :class:`FlagView` value-like wrappers, which ensure type safety. Any :ref:`value-like <lang-valuelike>` can also be explicitly wrapped in a view class by casting it to the enum type:
|
||||||
|
|
||||||
.. doctest::
|
.. doctest::
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import warnings
|
import warnings
|
||||||
from enum import Enum
|
from enum import Enum, EnumMeta
|
||||||
|
|
||||||
from amaranth.hdl.ast import *
|
from amaranth.hdl.ast import *
|
||||||
from amaranth.lib.enum import Enum as AmaranthEnum
|
from amaranth.lib.enum import Enum as AmaranthEnum
|
||||||
|
@ -189,6 +189,44 @@ class ShapeCastableTestCase(FHDLTestCase):
|
||||||
self.assertEqual(Shape.cast(sc), unsigned(1))
|
self.assertEqual(Shape.cast(sc), unsigned(1))
|
||||||
|
|
||||||
|
|
||||||
|
class ShapeLikeTestCase(FHDLTestCase):
|
||||||
|
def test_construct(self):
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
ShapeLike()
|
||||||
|
|
||||||
|
def test_subclass(self):
|
||||||
|
self.assertTrue(issubclass(Shape, ShapeLike))
|
||||||
|
self.assertTrue(issubclass(MockShapeCastable, ShapeLike))
|
||||||
|
self.assertTrue(issubclass(int, ShapeLike))
|
||||||
|
self.assertTrue(issubclass(range, ShapeLike))
|
||||||
|
self.assertTrue(issubclass(EnumMeta, ShapeLike))
|
||||||
|
self.assertFalse(issubclass(Enum, ShapeLike))
|
||||||
|
self.assertFalse(issubclass(str, ShapeLike))
|
||||||
|
self.assertTrue(issubclass(ShapeLike, ShapeLike))
|
||||||
|
|
||||||
|
def test_isinstance(self):
|
||||||
|
self.assertTrue(isinstance(unsigned(2), ShapeLike))
|
||||||
|
self.assertTrue(isinstance(MockShapeCastable(unsigned(2)), ShapeLike))
|
||||||
|
self.assertTrue(isinstance(2, ShapeLike))
|
||||||
|
self.assertTrue(isinstance(0, ShapeLike))
|
||||||
|
self.assertFalse(isinstance(-1, ShapeLike))
|
||||||
|
self.assertTrue(isinstance(range(10), ShapeLike))
|
||||||
|
self.assertFalse(isinstance("abc", ShapeLike))
|
||||||
|
|
||||||
|
def test_isinstance_enum(self):
|
||||||
|
class EnumA(Enum):
|
||||||
|
A = 1
|
||||||
|
B = 2
|
||||||
|
class EnumB(Enum):
|
||||||
|
A = "a"
|
||||||
|
B = "b"
|
||||||
|
class EnumC(Enum):
|
||||||
|
A = Cat(Const(1, 2), Const(0, 2))
|
||||||
|
self.assertTrue(isinstance(EnumA, ShapeLike))
|
||||||
|
self.assertFalse(isinstance(EnumB, ShapeLike))
|
||||||
|
self.assertTrue(isinstance(EnumC, ShapeLike))
|
||||||
|
|
||||||
|
|
||||||
class ValueTestCase(FHDLTestCase):
|
class ValueTestCase(FHDLTestCase):
|
||||||
def test_cast(self):
|
def test_cast(self):
|
||||||
self.assertIsInstance(Value.cast(0), Const)
|
self.assertIsInstance(Value.cast(0), Const)
|
||||||
|
@ -1300,6 +1338,50 @@ class ValueCastableTestCase(FHDLTestCase):
|
||||||
self.assertIsInstance(Value.cast(vc), Signal)
|
self.assertIsInstance(Value.cast(vc), Signal)
|
||||||
|
|
||||||
|
|
||||||
|
class ValueLikeTestCase(FHDLTestCase):
|
||||||
|
def test_construct(self):
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
ValueLike()
|
||||||
|
|
||||||
|
def test_subclass(self):
|
||||||
|
self.assertTrue(issubclass(Value, ValueLike))
|
||||||
|
self.assertTrue(issubclass(MockValueCastable, ValueLike))
|
||||||
|
self.assertTrue(issubclass(int, ValueLike))
|
||||||
|
self.assertFalse(issubclass(range, ValueLike))
|
||||||
|
self.assertFalse(issubclass(EnumMeta, ValueLike))
|
||||||
|
self.assertTrue(issubclass(Enum, ValueLike))
|
||||||
|
self.assertFalse(issubclass(str, ValueLike))
|
||||||
|
self.assertTrue(issubclass(ValueLike, ValueLike))
|
||||||
|
|
||||||
|
def test_isinstance(self):
|
||||||
|
self.assertTrue(isinstance(Const(0, 2), ValueLike))
|
||||||
|
self.assertTrue(isinstance(MockValueCastable(Const(0, 2)), ValueLike))
|
||||||
|
self.assertTrue(isinstance(2, ValueLike))
|
||||||
|
self.assertTrue(isinstance(-2, ValueLike))
|
||||||
|
self.assertFalse(isinstance(range(10), ValueLike))
|
||||||
|
|
||||||
|
def test_enum(self):
|
||||||
|
class EnumA(Enum):
|
||||||
|
A = 1
|
||||||
|
B = 2
|
||||||
|
class EnumB(Enum):
|
||||||
|
A = "a"
|
||||||
|
B = "b"
|
||||||
|
class EnumC(Enum):
|
||||||
|
A = Cat(Const(1, 2), Const(0, 2))
|
||||||
|
class EnumD(Enum):
|
||||||
|
A = 1
|
||||||
|
B = "a"
|
||||||
|
self.assertTrue(issubclass(EnumA, ValueLike))
|
||||||
|
self.assertFalse(issubclass(EnumB, ValueLike))
|
||||||
|
self.assertTrue(issubclass(EnumC, ValueLike))
|
||||||
|
self.assertFalse(issubclass(EnumD, ValueLike))
|
||||||
|
self.assertTrue(isinstance(EnumA.A, ValueLike))
|
||||||
|
self.assertFalse(isinstance(EnumB.A, ValueLike))
|
||||||
|
self.assertTrue(isinstance(EnumC.A, ValueLike))
|
||||||
|
self.assertFalse(isinstance(EnumD.A, ValueLike))
|
||||||
|
|
||||||
|
|
||||||
class SampleTestCase(FHDLTestCase):
|
class SampleTestCase(FHDLTestCase):
|
||||||
@_ignore_deprecated
|
@_ignore_deprecated
|
||||||
def test_const(self):
|
def test_const(self):
|
||||||
|
|
Loading…
Reference in a new issue