air_types/
types.rs

1use num::BigInt;
2use std::fmt::{Debug, Display, Formatter};
3use std::num::{NonZero, NonZeroU8};
4
5/// Type to use as convenience
6pub const BOOL: Type = Type::int(1);
7/// Type to use as convenience
8pub const I8: Type = Type::int(8);
9/// Type to use as convenience
10pub const I16: Type = Type::int(16);
11/// Type to use as convenience
12pub const I32: Type = Type::int(32);
13/// Type to use as convenience
14pub const I64: Type = Type::int(64);
15
16/// All non-void types
17pub const TYPES: [Type; 5] = [BOOL, I8, I16, I32, I64];
18
19/// Represents a type
20#[derive(Copy, Clone, Eq, PartialEq, Hash)]
21pub enum Type {
22    /// An integer type with an associated width
23    Int(NonZeroU8),
24}
25
26impl Type {
27    /// Return the bit width of a value
28    pub fn bit_width(&self) -> u32 {
29        match self {
30            Type::Int(w) => w.get().into(),
31        }
32    }
33}
34
35impl Type {
36    /// Create a new integer type
37    pub const fn int(n: u8) -> Self {
38        if n == 0 {
39            panic!("Cannot have i0");
40        }
41
42        // Safety: we know the value is not zero
43        Self::Int(unsafe { NonZero::new_unchecked(n) })
44    }
45
46    /// Returns true if self is bool
47    #[inline]
48    pub fn is_bool(&self) -> bool {
49        matches!(self, Self::Int(w) if w.get() == 1)
50    }
51
52    /// The maximum value that can be stored in the type.
53    #[inline]
54    pub fn max_val(&self) -> BigInt {
55        match self {
56            Type::Int(w) => (BigInt::from(1) << w.get()) - 1,
57        }
58    }
59}
60
61impl Display for Type {
62    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
63        match self {
64            Type::Int(w) => write!(f, "i{}", w),
65        }
66    }
67}
68
69impl Debug for Type {
70    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
71        match self {
72            Type::Int(w) => write!(f, "I{}", w),
73        }
74    }
75}
76
77impl TryFrom<&str> for Type {
78    type Error = ();
79
80    fn try_from(value: &str) -> Result<Self, Self::Error> {
81        match value {
82            "i1" | "bool" => Ok(BOOL),
83            "i8" => Ok(I8),
84            "i16" => Ok(I16),
85            "i32" => Ok(I32),
86            "i64" => Ok(I64),
87            _ => Err(()),
88        }
89    }
90}
91
92#[cfg(feature = "arbitrary")]
93impl<'a> arbitrary::Arbitrary<'a> for Type {
94    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
95        let variant = u.int_in_range(0..=4)?;
96        let ty = match variant {
97            0 => BOOL,
98            1 => I8,
99            2 => I16,
100            3 => I32,
101            4 => I64,
102            _ => unreachable!(),
103        };
104
105        Ok(ty)
106    }
107}
108
109#[cfg(test)]
110mod tests {
111    use super::*;
112
113    #[test]
114    fn test_display() {
115        let actual = &[BOOL, I8, I16, I32, I64]
116            .iter()
117            .map(|ty| ty.to_string())
118            .collect::<Vec<_>>()
119            .join(", ");
120        let expected = "i1, i8, i16, i32, i64";
121        assert_eq!(actual, expected)
122    }
123
124    #[test]
125    fn test_debug() {
126        let actual = &[BOOL, I8, I16, I32, I64]
127            .iter()
128            .map(|ty| format!("{ty:?}"))
129            .collect::<Vec<_>>()
130            .join(", ");
131        let expected = "I1, I8, I16, I32, I64";
132        assert_eq!(actual, expected)
133    }
134}