elvish/pkg/eval/vals/cmp.go
Qi Xiao 25e27d6bb6 pkg/eval/vals: Make Cmp(a, b) return CmpEqual if Equal(a, b).
Also match the order of branches in Cmp and Equal.
2023-05-18 10:45:42 +01:00

127 lines
2.4 KiB
Go

package vals
import (
"math"
"math/big"
)
// Basic predicate commands.
// Ordering relationship between two Elvish values.
type Ordering uint8
// Possible Ordering values.
const (
CmpLess Ordering = iota
CmpEqual
CmpMore
CmpUncomparable
)
// Cmp compares two Elvish values and returns the ordering relationship between
// them. Cmp(a, b) returns CmpEqual iff Equal(a, b) returns true.
func Cmp(a, b any) Ordering {
// Keep the branches in the same order as [Equal].
switch a := a.(type) {
case nil:
if b == nil {
return CmpEqual
}
case bool:
if b, ok := b.(bool); ok {
switch {
case a == b:
return CmpEqual
//lint:ignore S1002 using booleans as values, not conditions
case a == false: // b == true is implicit
return CmpLess
default: // a == true && b == false
return CmpMore
}
}
case int, *big.Int, *big.Rat, float64:
switch b.(type) {
case int, *big.Int, *big.Rat, float64:
a, b := UnifyNums2(a, b, 0)
switch a := a.(type) {
case int:
return compareInt(a, b.(int))
case *big.Int:
return compareInt(a.Cmp(b.(*big.Int)), 0)
case *big.Rat:
return compareInt(a.Cmp(b.(*big.Rat)), 0)
case float64:
return compareFloat(a, b.(float64))
default:
panic("unreachable")
}
}
case string:
if b, ok := b.(string); ok {
switch {
case a == b:
return CmpEqual
case a < b:
return CmpLess
default: // a > b
return CmpMore
}
}
case List:
if b, ok := b.(List); ok {
aIt := a.Iterator()
bIt := b.Iterator()
for aIt.HasElem() && bIt.HasElem() {
o := Cmp(aIt.Elem(), bIt.Elem())
if o != CmpEqual {
return o
}
aIt.Next()
bIt.Next()
}
switch {
case a.Len() == b.Len():
return CmpEqual
case a.Len() < b.Len():
return CmpLess
default: // a.Len() > b.Len()
return CmpMore
}
}
default:
if Equal(a, b) {
return CmpEqual
}
}
return CmpUncomparable
}
func compareInt(a, b int) Ordering {
if a < b {
return CmpLess
} else if a > b {
return CmpMore
}
return CmpEqual
}
func compareFloat(a, b float64) Ordering {
// For the sake of ordering, NaN's are considered equal to each
// other and smaller than all numbers
switch {
case math.IsNaN(a):
if math.IsNaN(b) {
return CmpEqual
}
return CmpLess
case math.IsNaN(b):
return CmpMore
case a < b:
return CmpLess
case a > b:
return CmpMore
default: // a == b
return CmpEqual
}
}