nanvm_lib/vm/any/
relational.rs1use core::cmp::Ordering;
2
3use crate::vm::{
4 Any, IVm, ToAny, Unpacked, error, numeric_coercion::to_numeric, primitive::Primitive,
5 primitive_coercion::ToPrimitivePreferredType, string_numeric_literal::string_to_bigint,
6};
7
8impl<A: IVm> Any<A> {
9 pub fn lt(self, rhs: Self) -> Result<Self, Self> {
12 Ok(matches!(compare(self, rhs)?, Some(Ordering::Less)).to_any())
13 }
14
15 pub fn gt(self, rhs: Self) -> Result<Self, Self> {
17 Ok(matches!(compare(self, rhs)?, Some(Ordering::Greater)).to_any())
18 }
19
20 pub fn le(self, rhs: Self) -> Result<Self, Self> {
22 Ok(matches!(compare(self, rhs)?, Some(Ordering::Less | Ordering::Equal)).to_any())
23 }
24
25 pub fn ge(self, rhs: Self) -> Result<Self, Self> {
27 Ok(matches!(
28 compare(self, rhs)?,
29 Some(Ordering::Greater | Ordering::Equal)
30 )
31 .to_any())
32 }
33}
34
35fn compare<A: IVm>(x: Any<A>, y: Any<A>) -> Result<Option<Ordering>, Any<A>> {
45 let px = to_primitive_or_text(x)?;
46 match (px, to_primitive_or_text(y)?) {
47 (None, None) | (None, Some(Primitive::String(_))) | (Some(Primitive::String(_)), None) => {
49 Err(error::function_text())
50 }
51 (None, Some(_)) | (Some(_), None) => Ok(None),
54 (Some(px), Some(py)) => compare_primitives(px, py),
55 }
56}
57
58fn to_primitive_or_text<A: IVm>(v: Any<A>) -> Result<Option<Primitive<A>>, Any<A>> {
64 match v.clone().into() {
65 Unpacked::Function(f) => Ok(f.text().ok().map(Primitive::String)),
66 _ => v
67 .to_primitive(Some(ToPrimitivePreferredType::Number))
68 .map(Some),
69 }
70}
71
72fn compare_primitives<A: IVm>(
73 px: Primitive<A>,
74 py: Primitive<A>,
75) -> Result<Option<Ordering>, Any<A>> {
76 match (px, py) {
77 (Primitive::String(sx), Primitive::String(sy)) => Ok(Some(sx.cmp(&sy))),
78 (Primitive::BigInt(bx), Primitive::String(sy)) => {
79 let s: std::string::String = sy.into();
80 Ok(string_to_bigint(&s)?.map(|by| bx.cmp(&by)))
81 }
82 (Primitive::String(sx), Primitive::BigInt(by)) => {
83 let s: std::string::String = sx.into();
84 Ok(string_to_bigint(&s)?.map(|bx| bx.cmp(&by)))
85 }
86 (px, py) => Ok(to_numeric(px)?.compare(&to_numeric(py)?)),
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use crate::{
93 naive::Naive,
94 vm::{Any, BigInt, ToAny},
95 };
96
97 type A = Naive;
98
99 fn n(v: f64) -> Any<A> {
100 v.to_any()
101 }
102
103 fn big(v: i64) -> Any<A> {
104 Into::<BigInt<A>>::into(v).to_any()
105 }
106
107 fn bool_of(r: Result<Any<A>, Any<A>>) -> bool {
108 bool::try_from(r.unwrap()).unwrap()
109 }
110
111 #[test]
112 fn number_lt_number() {
113 assert!(bool_of(n(1.0).lt(n(2.0))));
114 assert!(!bool_of(n(2.0).lt(n(1.0))));
115 }
116
117 #[test]
118 fn nan_is_never_less() {
119 assert!(!bool_of(n(f64::NAN).lt(n(1.0))));
120 assert!(!bool_of(n(1.0).lt(n(f64::NAN))));
121 assert!(!bool_of(n(f64::NAN).le(n(1.0))));
122 assert!(!bool_of(n(1.0).le(n(f64::NAN))));
123 }
124
125 #[test]
126 fn nan_is_never_greater() {
127 assert!(!bool_of(n(f64::NAN).gt(n(1.0))));
128 assert!(!bool_of(n(1.0).gt(n(f64::NAN))));
129 assert!(!bool_of(n(f64::NAN).ge(n(1.0))));
130 assert!(!bool_of(big(1).ge(n(f64::NAN))));
131 }
132
133 #[test]
134 fn number_vs_bigint_each_operator() {
135 assert!(bool_of(n(5.5).gt(big(5))));
136 assert!(!bool_of(n(5.0).gt(big(5))));
137 assert!(bool_of(n(5.0).ge(big(5))));
138 assert!(!bool_of(n(4.5).ge(big(5))));
139 assert!(bool_of(n(4.5).lt(big(5))));
140 assert!(bool_of(n(5.0).le(big(5))));
141 assert!(bool_of(n(f64::INFINITY).gt(big(5))));
142 assert!(bool_of(n(f64::NEG_INFINITY).lt(big(5))));
143 }
144
145 #[test]
146 fn string_lexicographic() {
147 let s = |v: &str| -> Any<A> { v.into() };
148 assert!(bool_of(s("10").lt(s("9"))));
149 assert!(!bool_of(s("9").lt(s("10"))));
150 }
151
152 #[test]
153 fn bigint_vs_number_exact() {
154 assert!(bool_of(big(5).lt(n(5.5))));
155 assert!(!bool_of(big(5).lt(n(5.0))));
156 assert!(bool_of(big(5).le(n(5.0))));
157 assert!(bool_of(big(5).lt(n(f64::INFINITY))));
158 assert!(!bool_of(big(5).lt(n(f64::NEG_INFINITY))));
159 }
160
161 #[test]
162 fn large_bigint_vs_large_number_exact() {
163 assert!(bool_of(
167 big(1i64 << 60).lt(n((1u64 << 60) as f64 + 2_048.0))
168 ));
169 }
170
171 #[test]
172 fn string_to_bigint_comparison() {
173 let s = |v: &str| -> Any<A> { v.into() };
174 assert!(bool_of(s("10").lt(big(20))));
175 assert!(bool_of(big(20).lt(s("30"))));
176 assert!(!bool_of(s("abc").lt(big(20))));
177 assert!(!bool_of(big(20).lt(s("abc"))));
178 }
179}