174 lines
6.6 KiB
Rust
174 lines
6.6 KiB
Rust
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<ConstantType> 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<Self, Self::Err> {
|
|
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<Value, PrimOpError> {
|
|
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,
|
|
}
|
|
}
|
|
}
|