use num::BigInt;
use std::fmt::{Debug, Display, Formatter};
use std::num::{NonZero, NonZeroU8};
pub const VOID: Type = Type::Void;
pub const BOOL: Type = Type::int(1);
pub const I8: Type = Type::int(8);
pub const I16: Type = Type::int(16);
pub const I32: Type = Type::int(32);
pub const I64: Type = Type::int(64);
pub const TYPES: [Type; 5] = [BOOL, I8, I16, I32, I64];
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
pub enum Type {
Void,
Int(NonZeroU8),
}
impl Type {
pub fn bit_width(&self) -> u32 {
match self {
Type::Void => 0,
Type::Int(w) => w.get().into(),
}
}
}
impl Type {
pub const fn int(n: u8) -> Self {
if n == 0 {
panic!("Cannot have i0");
}
Self::Int(unsafe { NonZero::new_unchecked(n) })
}
#[inline]
pub fn is_void(&self) -> bool {
matches!(self, Self::Void)
}
#[inline]
pub fn is_bool(&self) -> bool {
matches!(self, Self::Int(w) if w.get() == 1)
}
#[inline]
pub fn max_val(&self) -> BigInt {
match self {
Type::Void => panic!("Cannot get max value of void"),
Type::Int(w) => (BigInt::from(1) << w.get()) - 1,
}
}
}
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),
}
}
}
impl TryFrom<&str> for Type {
type Error = ();
fn try_from(value: &str) -> Result<Self, Self::Error> {
match value {
"void" => Ok(Type::Void),
"i1" | "bool" => Ok(BOOL),
"i8" => Ok(I8),
"i16" => Ok(I16),
"i32" => Ok(I32),
"i64" => Ok(I64),
_ => Err(()),
}
}
}
#[cfg(feature = "arbitrary")]
impl<'a> arbitrary::Arbitrary<'a> for Type {
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
let variant = u.int_in_range(0..=4)?;
let ty = match variant {
0 => BOOL,
1 => I8,
2 => I16,
3 => I32,
4 => I64,
_ => unreachable!(),
};
Ok(ty)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_display() {
let actual = &[VOID, BOOL, 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, BOOL, I8, I16, I32, I64]
.iter()
.map(|ty| format!("{ty:?}"))
.collect::<Vec<_>>()
.join(", ");
let expected = "VOID, I1, I8, I16, I32, I64";
assert_eq!(actual, expected)
}
}