stack_assembly/
value.rs

1use std::fmt;
2
3/// # A unit of data
4///
5/// StackAssembly is an _untyped_ languages. All of its values, both on the
6/// [`Stack`] and in [`Memory`], are 32 bits wide. Depending on the situation,
7/// they may be interpreted as unsigned or signed.
8///
9/// You can create an instance of `Value` through its `From` implementations.
10///
11/// ```
12/// use stack_assembly::Value;
13///
14/// Value::from(3i32);
15/// Value::from(5u32);
16/// ```
17///
18/// [`Stack`]: crate::Stack
19/// [`Memory`]: crate::Memory
20#[derive(Clone, Copy, Eq, PartialEq, bytemuck::Pod, bytemuck::Zeroable)]
21#[repr(transparent)]
22pub struct Value {
23    inner: u32,
24}
25
26impl Value {
27    /// # Convert the value to an `i32`
28    ///
29    /// Since all values are 32 bits wide, this is always possible. Interprets
30    /// the bits of the value as a signed (two's complement) integer.
31    pub fn to_i32(self) -> i32 {
32        i32::from_le_bytes(self.inner.to_le_bytes())
33    }
34
35    /// # Convert the value to a `u32`
36    ///
37    /// Since all values are 32 bits wide, this is always possible. Interprets
38    /// the bits of the value as an unsigned integer.
39    pub fn to_u32(self) -> u32 {
40        self.inner
41    }
42
43    /// # Convert to a `usize`
44    ///
45    /// This is usually possible, unless this library runs on a platform where
46    /// `usize` is less than 32 bits wide. That is considered a niche use case
47    /// which is not fully supported.
48    ///
49    /// ## Panics
50    ///
51    /// Panics, if `usize` can not represent this value.
52    pub fn to_usize(self) -> usize {
53        let Ok(index) = self.inner.try_into() else {
54            panic!(
55                "Can't convert value `{value}` to `usize`. This can only \
56                happen  on platforms where Rust's `usize` is less than 32 bits \
57                bits wide. This is a niche use case that isn't fully \
58                supported, making this panic an acceptable outcome.\n\
59                \n\
60                Additionally, since `usize` is only used for storage of values \
61                or operators, the value was invalid in the first place \
62                (meaning the StackAssembly program has a bug), as it wouldn't \
63                be possible to store as many item as the value implies should \
64                be there.",
65                value = self.inner,
66            );
67        };
68
69        index
70    }
71}
72
73impl From<i32> for Value {
74    fn from(value: i32) -> Self {
75        let inner = u32::from_le_bytes(value.to_le_bytes());
76        Self { inner }
77    }
78}
79
80impl From<u32> for Value {
81    fn from(inner: u32) -> Self {
82        Self { inner }
83    }
84}
85
86impl fmt::Debug for Value {
87    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
88        // Let's bypass this type and format the inner value. This is just a
89        // wrapper anyway, and showing it in debug output is unnecessary noise.
90        self.inner.fmt(f)
91    }
92}