hdl.ast: recursively cast ValueCastable objects to values.
This commit is contained in:
parent
3b799481f7
commit
0723f6bac9
|
@ -135,16 +135,22 @@ class Value(metaclass=ABCMeta):
|
||||||
|
|
||||||
Booleans and integers are wrapped into a :class:`Const`. Enumerations whose members are
|
Booleans and integers are wrapped into a :class:`Const`. Enumerations whose members are
|
||||||
all integers are converted to a :class:`Const` with a shape that fits every member.
|
all integers are converted to a :class:`Const` with a shape that fits every member.
|
||||||
|
:class:`ValueCastable` objects are recursively cast to an Amaranth value.
|
||||||
"""
|
"""
|
||||||
if isinstance(obj, Value):
|
while True:
|
||||||
return obj
|
if isinstance(obj, Value):
|
||||||
if isinstance(obj, int):
|
return obj
|
||||||
return Const(obj)
|
elif isinstance(obj, int):
|
||||||
if isinstance(obj, Enum):
|
return Const(obj)
|
||||||
return Const(obj.value, Shape.cast(type(obj)))
|
elif isinstance(obj, Enum):
|
||||||
if isinstance(obj, ValueCastable):
|
return Const(obj.value, Shape.cast(type(obj)))
|
||||||
return obj.as_value()
|
elif isinstance(obj, ValueCastable):
|
||||||
raise TypeError("Object {!r} cannot be converted to an Amaranth value".format(obj))
|
new_obj = obj.as_value()
|
||||||
|
else:
|
||||||
|
raise TypeError("Object {!r} cannot be converted to an Amaranth value".format(obj))
|
||||||
|
if new_obj is obj:
|
||||||
|
raise RecursionError("Value-castable object {!r} casts to itself".format(obj))
|
||||||
|
obj = new_obj
|
||||||
|
|
||||||
def __init__(self, *, src_loc_at=0):
|
def __init__(self, *, src_loc_at=0):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
@ -1276,7 +1282,7 @@ class UserValue(Value):
|
||||||
|
|
||||||
|
|
||||||
class ValueCastable:
|
class ValueCastable:
|
||||||
"""Base class for classes which can be cast to Values.
|
"""Interface of objects can be cast to :class:`Value`s.
|
||||||
|
|
||||||
A ``ValueCastable`` can be cast to ``Value``, meaning its precise representation does not have
|
A ``ValueCastable`` can be cast to ``Value``, meaning its precise representation does not have
|
||||||
to be immediately known. This is useful in certain metaprogramming scenarios. Instead of
|
to be immediately known. This is useful in certain metaprogramming scenarios. Instead of
|
||||||
|
|
|
@ -220,7 +220,7 @@ Value casting
|
||||||
|
|
||||||
Like shapes, values may be *cast* from other objects, which are called *value-castable*. Casting 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-castable*. Casting allows objects that are not provided by Amaranth, such as integers or enumeration members, to be used in Amaranth expressions directly.
|
||||||
|
|
||||||
.. TODO: link to UserValue
|
.. 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-castable objects are accepted anywhere values are.
|
||||||
|
|
||||||
|
|
|
@ -1060,6 +1060,15 @@ class UserValueTestCase(FHDLTestCase):
|
||||||
self.assertEqual(uv.lower_count, 1)
|
self.assertEqual(uv.lower_count, 1)
|
||||||
|
|
||||||
|
|
||||||
|
class MockValueCastable(ValueCastable):
|
||||||
|
def __init__(self, dest):
|
||||||
|
self.dest = dest
|
||||||
|
|
||||||
|
@ValueCastable.lowermethod
|
||||||
|
def as_value(self):
|
||||||
|
return self.dest
|
||||||
|
|
||||||
|
|
||||||
class MockValueCastableChanges(ValueCastable):
|
class MockValueCastableChanges(ValueCastable):
|
||||||
def __init__(self, width=0):
|
def __init__(self, width=0):
|
||||||
self.width = width
|
self.width = width
|
||||||
|
@ -1097,14 +1106,14 @@ class MockValueCastableCustomGetattr(ValueCastable):
|
||||||
class ValueCastableTestCase(FHDLTestCase):
|
class ValueCastableTestCase(FHDLTestCase):
|
||||||
def test_not_decorated(self):
|
def test_not_decorated(self):
|
||||||
with self.assertRaisesRegex(TypeError,
|
with self.assertRaisesRegex(TypeError,
|
||||||
r"^Class 'MockValueCastableNotDecorated' deriving from `ValueCastable` must decorate the `as_value` "
|
r"^Class 'MockValueCastableNotDecorated' deriving from `ValueCastable` must "
|
||||||
r"method with the `ValueCastable.lowermethod` decorator$"):
|
r"decorate the `as_value` method with the `ValueCastable.lowermethod` decorator$"):
|
||||||
vc = MockValueCastableNotDecorated()
|
vc = MockValueCastableNotDecorated()
|
||||||
|
|
||||||
def test_no_override(self):
|
def test_no_override(self):
|
||||||
with self.assertRaisesRegex(TypeError,
|
with self.assertRaisesRegex(TypeError,
|
||||||
r"^Class 'MockValueCastableNoOverride' deriving from `ValueCastable` must override the `as_value` "
|
r"^Class 'MockValueCastableNoOverride' deriving from `ValueCastable` must "
|
||||||
r"method$"):
|
r"override the `as_value` method$"):
|
||||||
vc = MockValueCastableNoOverride()
|
vc = MockValueCastableNoOverride()
|
||||||
|
|
||||||
def test_memoized(self):
|
def test_memoized(self):
|
||||||
|
@ -1121,6 +1130,17 @@ class ValueCastableTestCase(FHDLTestCase):
|
||||||
vc = MockValueCastableCustomGetattr()
|
vc = MockValueCastableCustomGetattr()
|
||||||
vc.as_value() # shouldn't call __getattr__
|
vc.as_value() # shouldn't call __getattr__
|
||||||
|
|
||||||
|
def test_recurse_bad(self):
|
||||||
|
vc = MockValueCastable(None)
|
||||||
|
vc.dest = vc
|
||||||
|
with self.assertRaisesRegex(RecursionError,
|
||||||
|
r"^Value-castable object <.+> casts to itself$"):
|
||||||
|
Value.cast(vc)
|
||||||
|
|
||||||
|
def test_recurse(self):
|
||||||
|
vc = MockValueCastable(MockValueCastable(Signal()))
|
||||||
|
self.assertIsInstance(Value.cast(vc), Signal)
|
||||||
|
|
||||||
|
|
||||||
class SampleTestCase(FHDLTestCase):
|
class SampleTestCase(FHDLTestCase):
|
||||||
def test_const(self):
|
def test_const(self):
|
||||||
|
|
Loading…
Reference in a new issue