Files
ngr/src/eval/primtype.rs

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,
}
}
}