Implement lexicographic array comparison (#2827)

This commit is contained in:
PgBiel 2023-12-05 15:15:03 -03:00 committed by GitHub
parent 3960f8f7d9
commit 0ebce56b36
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 41 additions and 0 deletions

View File

@ -551,6 +551,7 @@ pub fn compare(lhs: &Value, rhs: &Value) -> StrResult<Ordering> {
(Duration(a), Duration(b)) => a.cmp(b),
(Datetime(a), Datetime(b)) => try_cmp_datetimes(a, b)?,
(Array(a), Array(b)) => try_cmp_arrays(a.as_slice(), b.as_slice())?,
_ => mismatch!("cannot compare {} and {}", lhs, rhs),
})
@ -568,6 +569,26 @@ fn try_cmp_datetimes(a: &Datetime, b: &Datetime) -> StrResult<Ordering> {
.ok_or_else(|| eco_format!("cannot compare {} and {}", a.kind(), b.kind()))
}
/// Try to compare arrays of values lexicographically.
fn try_cmp_arrays(a: &[Value], b: &[Value]) -> StrResult<Ordering> {
a.iter()
.zip(b.iter())
.find_map(|(first, second)| {
match compare(first, second) {
// Keep searching for a pair of elements that isn't equal.
Ok(Ordering::Equal) => None,
// Found a pair which either is not equal or not comparable, so
// we stop searching.
result => Some(result),
}
})
.unwrap_or_else(|| {
// The two arrays are equal up to the shortest array's extent,
// so compare their lengths instead.
Ok(a.len().cmp(&b.len()))
})
}
/// Test whether one value is "in" another one.
pub fn in_(lhs: Value, rhs: Value) -> StrResult<Value> {
if let Some(b) = contains(&lhs, &rhs) {

View File

@ -37,6 +37,14 @@
// Error: 3-22 cannot compare 2.2 with NaN
#(2.2 <= float("nan"))
---
// Error: 3-26 cannot compare integer and string
#((0, 1, 3) > (0, 1, "a"))
---
// Error: 3-42 cannot compare 3.5 with NaN
#((0, "a", 3.5) <= (0, "a", float("nan")))
---
// Error: 3-12 cannot divide by zero
#(1.2 / 0.0)

View File

@ -206,6 +206,18 @@
#test(50% < 40% + 0pt, false)
#test(40% + 0pt < 50% + 0pt, true)
#test(1em < 2em, true)
#test((0, 1, 2, 4) < (0, 1, 2, 5), true)
#test((0, 1, 2, 4) < (0, 1, 2, 3), false)
#test((0, 1, 2, 3.3) > (0, 1, 2, 4), false)
#test((0, 1, 2) < (0, 1, 2, 3), true)
#test((0, 1, "b") > (0, 1, "a", 3), true)
#test((0, 1.1, 3) >= (0, 1.1, 3), true)
#test((0, 1, datetime(day: 1, month: 12, year: 2023)) <= (0, 1, datetime(day: 1, month: 12, year: 2023), 3), true)
#test(("a", 23, 40, "b") > ("a", 23, 40), true)
#test(() <= (), true)
#test(() >= (), true)
#test(() <= (1,), true)
#test((1,) <= (), false)
---
// Test assignment operators.