use crate::{ eval::{PrimOpError, Value}, syntax::ConstantType, }; use std::{fmt::Display, str::FromStr}; #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum PrimitiveType { U8, U16, U32, U64, I8, I16, I32, I64, } impl Display for PrimitiveType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { PrimitiveType::I8 => write!(f, "i8"), PrimitiveType::I16 => write!(f, "i16"), PrimitiveType::I32 => write!(f, "i32"), PrimitiveType::I64 => write!(f, "i64"), PrimitiveType::U8 => write!(f, "u8"), PrimitiveType::U16 => write!(f, "u16"), PrimitiveType::U32 => write!(f, "u32"), PrimitiveType::U64 => write!(f, "u64"), } } } impl<'a> From<&'a Value> for PrimitiveType { fn from(value: &Value) -> Self { match value { Value::I8(_) => PrimitiveType::I8, Value::I16(_) => PrimitiveType::I16, Value::I32(_) => PrimitiveType::I32, Value::I64(_) => PrimitiveType::I64, Value::U8(_) => PrimitiveType::U8, Value::U16(_) => PrimitiveType::U16, Value::U32(_) => PrimitiveType::U32, Value::U64(_) => PrimitiveType::U64, } } } impl From for PrimitiveType { fn from(value: ConstantType) -> Self { match value { ConstantType::I8 => PrimitiveType::I8, ConstantType::I16 => PrimitiveType::I16, ConstantType::I32 => PrimitiveType::I32, ConstantType::I64 => PrimitiveType::I64, ConstantType::U8 => PrimitiveType::U8, ConstantType::U16 => PrimitiveType::U16, ConstantType::U32 => PrimitiveType::U32, ConstantType::U64 => PrimitiveType::U64, } } } impl FromStr for PrimitiveType { type Err = PrimOpError; fn from_str(s: &str) -> Result { match s { "i8" => Ok(PrimitiveType::I8), "i16" => Ok(PrimitiveType::I16), "i32" => Ok(PrimitiveType::I32), "i64" => Ok(PrimitiveType::I64), "u8" => Ok(PrimitiveType::U8), "u16" => Ok(PrimitiveType::U16), "u32" => Ok(PrimitiveType::U32), "u64" => Ok(PrimitiveType::U64), _ => Err(PrimOpError::UnknownPrimType(s.to_string())), } } } impl PrimitiveType { /// Return true if this type can be safely cast into the target type. pub fn can_cast_to(&self, target: &PrimitiveType) -> bool { match self { PrimitiveType::U8 => matches!( target, PrimitiveType::U8 | PrimitiveType::U16 | PrimitiveType::U32 | PrimitiveType::U64 | PrimitiveType::I16 | PrimitiveType::I32 | PrimitiveType::I64 ), PrimitiveType::U16 => matches!( target, PrimitiveType::U16 | PrimitiveType::U32 | PrimitiveType::U64 | PrimitiveType::I32 | PrimitiveType::I64 ), PrimitiveType::U32 => matches!( target, PrimitiveType::U32 | PrimitiveType::U64 | PrimitiveType::I64 ), PrimitiveType::U64 => target == &PrimitiveType::U64, PrimitiveType::I8 => matches!( target, PrimitiveType::I8 | PrimitiveType::I16 | PrimitiveType::I32 | PrimitiveType::I64 ), PrimitiveType::I16 => matches!( target, PrimitiveType::I16 | PrimitiveType::I32 | PrimitiveType::I64 ), PrimitiveType::I32 => matches!(target, PrimitiveType::I32 | PrimitiveType::I64), PrimitiveType::I64 => target == &PrimitiveType::I64, } } /// Try to cast the given value to this type, returning the new value. /// /// Returns an error if the cast is not safe *in* *general*. This means that /// this function will error even if the number will actually fit in the target /// type, but it would not be generally safe to cast a member of the given /// type to the target type. (So, for example, "1i64" is a number that could /// work as a "u64", but since negative numbers wouldn't work, a cast from /// "1i64" to "u64" will fail.) pub fn safe_cast(&self, source: &Value) -> Result { match (self, source) { (PrimitiveType::U8, Value::U8(x)) => Ok(Value::U8(*x)), (PrimitiveType::U16, Value::U8(x)) => Ok(Value::U16(*x as u16)), (PrimitiveType::U16, Value::U16(x)) => Ok(Value::U16(*x)), (PrimitiveType::U32, Value::U8(x)) => Ok(Value::U32(*x as u32)), (PrimitiveType::U32, Value::U16(x)) => Ok(Value::U32(*x as u32)), (PrimitiveType::U32, Value::U32(x)) => Ok(Value::U32(*x)), (PrimitiveType::U64, Value::U8(x)) => Ok(Value::U64(*x as u64)), (PrimitiveType::U64, Value::U16(x)) => Ok(Value::U64(*x as u64)), (PrimitiveType::U64, Value::U32(x)) => Ok(Value::U64(*x as u64)), (PrimitiveType::U64, Value::U64(x)) => Ok(Value::U64(*x)), (PrimitiveType::I8, Value::I8(x)) => Ok(Value::I8(*x)), (PrimitiveType::I16, Value::I8(x)) => Ok(Value::I16(*x as i16)), (PrimitiveType::I16, Value::I16(x)) => Ok(Value::I16(*x)), (PrimitiveType::I32, Value::I8(x)) => Ok(Value::I32(*x as i32)), (PrimitiveType::I32, Value::I16(x)) => Ok(Value::I32(*x as i32)), (PrimitiveType::I32, Value::I32(x)) => Ok(Value::I32(*x)), (PrimitiveType::I64, Value::I8(x)) => Ok(Value::I64(*x as i64)), (PrimitiveType::I64, Value::I16(x)) => Ok(Value::I64(*x as i64)), (PrimitiveType::I64, Value::I32(x)) => Ok(Value::I64(*x as i64)), (PrimitiveType::I64, Value::I64(x)) => Ok(Value::I64(*x)), _ => Err(PrimOpError::UnsafeCast { from: source.into(), to: *self, }), } } pub fn max_value(&self) -> u64 { match self { PrimitiveType::U8 => u8::MAX as u64, PrimitiveType::U16 => u16::MAX as u64, PrimitiveType::U32 => u32::MAX as u64, PrimitiveType::U64 => u64::MAX, PrimitiveType::I8 => i8::MAX as u64, PrimitiveType::I16 => i16::MAX as u64, PrimitiveType::I32 => i32::MAX as u64, PrimitiveType::I64 => i64::MAX as u64, } } }