1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
use std::fmt::{Debug, Display, Formatter};
use std::num::{NonZero, NonZeroU8};

/// Type to use as convenience
pub const VOID: Type = Type::Void;
/// Type to use as convenience
pub const I1: Type = Type::int(1);
/// Type to use as convenience
pub const I8: Type = Type::int(8);
/// Type to use as convenience
pub const I16: Type = Type::int(16);
/// Type to use as convenience
pub const I32: Type = Type::int(32);
/// Type to use as convenience
pub const I64: Type = Type::int(64);

/// Represents a type
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
pub enum Type {
    /// The void type
    Void,
    /// An integer type with an associated width
    Int(NonZeroU8),
}

impl Type {
    const fn int(n: u8) -> Self {
        if n == 0 {
            panic!("Cannot have i0");
        }

        // Safety: we know the value is not zero
        Self::Int(unsafe { NonZero::new_unchecked(n) })
    }

    /// Returns true if self is void
    pub fn is_void(&self) -> bool {
        matches!(self, Self::Void)
    }
}

impl Display for Type {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        match self {
            Type::Void => write!(f, "void"),
            Type::Int(w) => write!(f, "i{}", w),
        }
    }
}

impl Debug for Type {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        match self {
            Type::Void => write!(f, "VOID"),
            Type::Int(w) => write!(f, "I{}", w),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_display() {
        let actual = &[VOID, I1, I8, I16, I32, I64]
            .iter()
            .map(|ty| ty.to_string())
            .collect::<Vec<_>>()
            .join(", ");
        let expected = "void, i1, i8, i16, i32, i64";
        assert_eq!(actual, expected)
    }

    #[test]
    fn test_debug() {
        let actual = &[VOID, I1, I8, I16, I32, I64]
            .iter()
            .map(|ty| format!("{ty:?}"))
            .collect::<Vec<_>>()
            .join(", ");
        let expected = "VOID, I1, I8, I16, I32, I64";
        assert_eq!(actual, expected)
    }
}