diff --git a/types/src/lib.rs b/types/src/lib.rs index 817340a..ff46ff0 100644 --- a/types/src/lib.rs +++ b/types/src/lib.rs @@ -1,5 +1,6 @@ mod bool; mod char; +mod number; mod value; #[cfg(test)] diff --git a/types/src/number/integer.rs b/types/src/number/integer.rs new file mode 100644 index 0000000..2515b39 --- /dev/null +++ b/types/src/number/integer.rs @@ -0,0 +1,55 @@ +/* types/src/number/integer.rs + * Eryn Wells + */ + +use std::any::Any; +use value::*; +use super::*; + +#[derive(Debug, Eq, PartialEq)] +pub struct Integer(pub Int); + +impl Number for Integer { + fn convert_down(&self) -> Option> { None } +} + +impl Value for Integer { + fn as_value(&self) -> &Value { self } +} + +impl IsBool for Integer { } +impl IsChar for Integer { } + +impl IsNumber for Integer { + fn is_integer(&self) -> bool { true } +} + +impl ValueEq for Integer { + fn eq(&self, other: &Value) -> bool { + other.as_any().downcast_ref::().map_or(false, |x| x == self) + } + + fn as_any(&self) -> &Any { self } +} + +#[cfg(test)] +mod tests { + use super::Integer; + use value::*; + + #[test] + fn equal_integers_are_equal() { + assert_eq!(Integer(3), Integer(3)); + assert_ne!(Integer(12), Integer(9)); + assert_eq!(Integer(4).as_value(), Integer(4).as_value()); + assert_ne!(Integer(5).as_value(), Integer(7).as_value()); + } + + #[test] + fn integers_are_integers() { + assert_eq!(Integer(4).is_integer(), true); + assert_eq!(Integer(4).is_number(), true); + assert_eq!(Integer(6).is_char(), false); + assert_eq!(Integer(6).is_bool(), false); + } +} diff --git a/types/src/number/mod.rs b/types/src/number/mod.rs index 69baf07..665e86f 100644 --- a/types/src/number/mod.rs +++ b/types/src/number/mod.rs @@ -1,4 +1,4 @@ -/* number.rs +/* types/src/number/mod.rs * Eryn Wells */ @@ -6,19 +6,40 @@ /// /// Scheme numbers are complex, literally. +pub mod integer; +pub mod rational; + +pub use self::integer::Integer; +pub use self::rational::Rational; + +use std::any::Any; +use std::fmt::Debug; +use std::ops::Deref; + +use super::value::*; + type Int = i64; type Flt = f64; -trait Number { - fn is_number(&self) -> bool { true } - fn is_complex(&self) -> bool { false } - fn is_real(&self) -> bool { false } - fn is_rational(&self) -> bool { false } - fn is_integer(&self) -> bool { false } +trait Number: Debug + IsBool + IsChar + IsNumber + Value { + fn convert_down(&self) -> Option>; +} + +impl Value for Box { + fn as_value(&self) -> &Value { self.deref().as_value() } +} + +impl IsBool for Box { } +impl IsChar for Box { } +impl IsNumber for Box { } + +impl ValueEq for Box { + fn eq(&self, other: &Value) -> bool { + self.deref().eq(other) + } + fn as_any(&self) -> &Any { self } } -struct Integer(Int); -struct Rational(Int, Int); struct Real(Flt); struct Complex<'a>(&'a Number, &'a Number); diff --git a/types/src/number/rational.rs b/types/src/number/rational.rs new file mode 100644 index 0000000..b105807 --- /dev/null +++ b/types/src/number/rational.rs @@ -0,0 +1,80 @@ +/* types/src/number/rational.rs + * Eryn Wells + */ + +use std::any::Any; +use value::*; +use super::*; + +#[derive(Debug, Eq, PartialEq)] +pub struct Rational(pub Int, pub Int); + +impl Number for Rational { + fn convert_down(&self) -> Option> { + if self.1 == 1 { + Some(Box::new(Integer(self.0))) + } + else { + None + } + } +} + +impl Value for Rational { + fn as_value(&self) -> &Value { self } +} + +impl IsBool for Rational { } +impl IsChar for Rational { } + +impl IsNumber for Rational { + fn is_rational(&self) -> bool { true } +} + +impl ValueEq for Rational { + fn eq(&self, other: &Value) -> bool { + other.as_any().downcast_ref::().map_or(false, |x| x == self) + } + + fn as_any(&self) -> &Any { self } +} + +#[cfg(test)] +mod tests { + use std::ops::Deref; + use number::*; + use value::*; + + #[test] + fn equal_rationals_are_equal() { + assert_eq!(Rational(3, 2), Rational(3, 2)); + assert_ne!(Rational(12, 4), Rational(9, 7)); + assert_eq!(Rational(4, 5).as_value(), Rational(4, 5).as_value()); + assert_ne!(Rational(5, 6).as_value(), Rational(7, 6).as_value()); + } + + #[test] + fn rationals_are_rationals() { + assert_eq!(Rational(4, 3).is_rational(), true); + assert_eq!(Rational(4, 3).is_integer(), false); + assert_eq!(Rational(4, 3).is_number(), true); + assert_eq!(Rational(6, 8).is_char(), false); + assert_eq!(Rational(6, 9).is_bool(), false); + } + + #[test] + fn rationals_should_reduce_to_integers_where_possible() { + let rational_as_integer = Rational(3, 1).convert_down(); + assert!(rational_as_integer.is_some()); + // Oh my god this line is so dumb. + let rational_as_integer = rational_as_integer.unwrap(); + let rational_as_integer = rational_as_integer.as_value(); + assert_eq!(rational_as_integer.deref(), Integer(3).as_value()); + } + + #[test] + fn rationals_should_not_reduce_to_integers_where_impossible() { + let rational_as_integer = Rational(3, 2).convert_down(); + assert!(rational_as_integer.is_none()); + } +}