//! Module for types. use std::fmt; /// Enum of possible types for `Value`s. #[derive(Clone, Debug)] pub enum Type { /// A type used to indicate an empty Arr. Any, /// Null value. Null, /// A boolean type. Bool, /// A signed integer type. Int, /// A fractional type. Frac, /// A character type. Char, /// A string type. Str, /// An array type, containing the type of its sub-elements. Arr(Box), /// A tuple type, containing the types of its sub-elements. Tup(Vec), /// An object type. Obj, } impl Type { /// Returns true if this type is strictly the same as `other`. /// Usually you want to use `eq()` instead. pub fn is(&self, other: &Type) -> bool { use self::Type::*; match *self { Any => { if let Any = *other { true } else { false } } Null => { if let Null = *other { true } else { false } } Bool => { if let Bool = *other { true } else { false } } Int => { if let Int = *other { true } else { false } } Frac => { if let Frac = *other { true } else { false } } Char => { if let Char = *other { true } else { false } } Str => { if let Str = *other { true } else { false } } Obj => { if let Obj = *other { true } else { false } } Arr(ref t1) => { if let Arr(ref t2) = *other { t1.is(t2) } else { false } } Tup(ref tvec1) => { if let Tup(ref tvec2) = *other { if tvec1.len() != tvec2.len() { return false; } tvec1.iter().zip(tvec2.iter()).all(|(t1, t2)| t1.is(t2)) } else { false } } } } /// Returns true if this `Type` contains `Any`. pub fn has_any(&self) -> bool { match *self { Type::Any => true, Type::Arr(ref t) => Self::has_any(t), Type::Tup(ref tvec) => tvec.iter().any(|t| Self::has_any(t)), _ => false, } } /// Returns a type with the most specificity that can be applied to the two input types as well /// as `true` if the returned type is not maximally specific, that is, it contains `Any`. If no /// single type can be applied to both input types (e.g. the types are `Str` and `Int`), returns /// `None`. /// /// # Examples /// /// ``` /// # #[macro_use] extern crate over; /// # fn main() { /// /// use over::types::Type; /// use over::types::Type::*; /// use over::value::Value; /// /// let val1: Value = tup!(arr![], arr![2]).into(); /// let val2: Value = tup!(arr!['c'], arr![]).into(); /// /// let (specific_type, has_any) = /// Type::most_specific(&val1.get_type(), &val2.get_type()).unwrap(); /// /// assert_eq!(specific_type, Tup(vec![Arr(Box::new(Char)), Arr(Box::new(Int))])); /// assert!(!has_any); /// /// # } /// ``` pub fn most_specific(type1: &Type, type2: &Type) -> Option<(Type, bool)> { use self::Type::*; if let Any = *type2 { return Some((type1.clone(), type1.has_any())); } match *type1 { Any => Some((type2.clone(), type2.has_any())), Arr(ref t1) => { if let Arr(ref t2) = *type2 { Self::most_specific(t1, t2).map(|(t, any)| (Arr(Box::new(t)), any)) } else { None } } Tup(ref tvec1) => { if let Tup(ref tvec2) = *type2 { if tvec1.len() == tvec2.len() { let mut has_any = false; let tvec: Option> = tvec1 .iter() .zip(tvec2.iter()) .map(|(t1, t2)| { Self::most_specific(t1, t2).map(|(t, any)| { if !has_any && any { has_any = any; } t }) }) .collect(); tvec.map(|tvec| (Tup(tvec), has_any)) } else { None } } else { None } } ref t => { if t == type2 { Some((t.clone(), false)) } else { None } } } } } /// Two types are considered equal if one of them is Any or they have the same variant. /// In the case of `Arr` and `Tup`, the inner types are recursively checked for equality. impl PartialEq for Type { fn eq(&self, other: &Self) -> bool { use self::Type::*; // If either is Any, always return `true`. if let Any = *other { return true; } match *self { Any => true, Arr(ref box1) => { if let Arr(ref box2) = *other { box1 == box2 } else { false } } Tup(ref tvec1) => { if let Tup(ref tvec2) = *other { tvec1 == tvec2 } else { false } } _ => self.is(other), } } } impl Eq for Type {} impl fmt::Display for Type { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use self::Type::*; match *self { Any => write!(f, "Any"), Null => write!(f, "Null"), Bool => write!(f, "Bool"), Int => write!(f, "Int"), Frac => write!(f, "Frac"), Char => write!(f, "Char"), Str => write!(f, "Str"), Arr(ref boxxy) => write!(f, "Arr({})", boxxy), Tup(ref tvec) => write!( f, "Tup({})", match tvec.get(0) { Some(t1) => tvec .iter() .skip(1) .fold(format!("{}", t1), |s, t| format!("{}, {}", s, t)), None => String::from(""), } ), Obj => write!(f, "Obj"), } } }