2018-02-01 00:46:49 +08:00
|
|
|
package eval
|
|
|
|
|
2021-12-26 20:18:28 +08:00
|
|
|
import (
|
|
|
|
"math"
|
|
|
|
"math/big"
|
|
|
|
|
|
|
|
"src.elv.sh/pkg/eval/errs"
|
|
|
|
"src.elv.sh/pkg/eval/vals"
|
|
|
|
)
|
2018-02-01 00:46:49 +08:00
|
|
|
|
|
|
|
// Basic predicate commands.
|
|
|
|
|
2020-08-17 01:46:29 +08:00
|
|
|
func init() {
|
2022-03-20 23:50:25 +08:00
|
|
|
addBuiltinFns(map[string]any{
|
2021-12-26 20:18:28 +08:00
|
|
|
"bool": vals.Bool,
|
|
|
|
"not": not,
|
|
|
|
"is": is,
|
|
|
|
"eq": eq,
|
|
|
|
"not-eq": notEq,
|
|
|
|
"compare": compare,
|
2020-08-17 01:46:29 +08:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-03-20 23:50:25 +08:00
|
|
|
func not(v any) bool {
|
2020-08-17 01:46:29 +08:00
|
|
|
return !vals.Bool(v)
|
|
|
|
}
|
|
|
|
|
2022-03-20 23:50:25 +08:00
|
|
|
func is(args ...any) bool {
|
2020-08-17 01:46:29 +08:00
|
|
|
for i := 0; i+1 < len(args); i++ {
|
|
|
|
if args[i] != args[i+1] {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2022-03-20 23:50:25 +08:00
|
|
|
func eq(args ...any) bool {
|
2020-08-17 01:46:29 +08:00
|
|
|
for i := 0; i+1 < len(args); i++ {
|
|
|
|
if !vals.Equal(args[i], args[i+1]) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2022-03-20 23:50:25 +08:00
|
|
|
func notEq(args ...any) bool {
|
2018-02-01 00:46:49 +08:00
|
|
|
for i := 0; i+1 < len(args); i++ {
|
2018-02-15 17:14:05 +08:00
|
|
|
if vals.Equal(args[i], args[i+1]) {
|
2018-02-04 14:30:36 +08:00
|
|
|
return false
|
2018-02-01 00:46:49 +08:00
|
|
|
}
|
|
|
|
}
|
2018-02-04 14:30:36 +08:00
|
|
|
return true
|
2018-02-01 00:46:49 +08:00
|
|
|
}
|
2021-12-26 20:18:28 +08:00
|
|
|
|
|
|
|
// ErrUncomparable is raised by the compare and order commands when inputs contain
|
|
|
|
// uncomparable values.
|
|
|
|
var ErrUncomparable = errs.BadValue{
|
|
|
|
What: `inputs to "compare" or "order"`,
|
|
|
|
Valid: "comparable values", Actual: "uncomparable values"}
|
|
|
|
|
2022-03-20 23:50:25 +08:00
|
|
|
func compare(fm *Frame, a, b any) (int, error) {
|
2021-12-26 20:18:28 +08:00
|
|
|
switch cmp(a, b) {
|
|
|
|
case less:
|
|
|
|
return -1, nil
|
|
|
|
case equal:
|
|
|
|
return 0, nil
|
|
|
|
case more:
|
|
|
|
return 1, nil
|
|
|
|
default:
|
|
|
|
return 0, ErrUncomparable
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type ordering uint8
|
|
|
|
|
|
|
|
const (
|
|
|
|
less ordering = iota
|
|
|
|
equal
|
|
|
|
more
|
|
|
|
uncomparable
|
|
|
|
)
|
|
|
|
|
2022-03-20 23:50:25 +08:00
|
|
|
func cmp(a, b any) ordering {
|
2021-12-26 20:18:28 +08:00
|
|
|
switch a := a.(type) {
|
|
|
|
case int, *big.Int, *big.Rat, float64:
|
|
|
|
switch b.(type) {
|
|
|
|
case int, *big.Int, *big.Rat, float64:
|
|
|
|
a, b := vals.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 equal
|
|
|
|
case a < b:
|
|
|
|
return less
|
|
|
|
default: // a > b
|
|
|
|
return more
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case vals.List:
|
|
|
|
if b, ok := b.(vals.List); ok {
|
|
|
|
aIt := a.Iterator()
|
|
|
|
bIt := b.Iterator()
|
|
|
|
for aIt.HasElem() && bIt.HasElem() {
|
|
|
|
o := cmp(aIt.Elem(), bIt.Elem())
|
|
|
|
if o != equal {
|
|
|
|
return o
|
|
|
|
}
|
|
|
|
aIt.Next()
|
|
|
|
bIt.Next()
|
|
|
|
}
|
|
|
|
switch {
|
|
|
|
case a.Len() == b.Len():
|
|
|
|
return equal
|
|
|
|
case a.Len() < b.Len():
|
|
|
|
return less
|
|
|
|
default: // a.Len() > b.Len()
|
|
|
|
return more
|
|
|
|
}
|
|
|
|
}
|
2022-08-29 19:25:54 +08:00
|
|
|
case bool:
|
|
|
|
if b, ok := b.(bool); ok {
|
|
|
|
switch {
|
|
|
|
case a == b:
|
|
|
|
return equal
|
2022-08-29 19:29:24 +08:00
|
|
|
//lint:ignore S1002 using booleans as values, not conditions
|
|
|
|
case a == false: // b == true is implicit
|
2022-08-29 19:25:54 +08:00
|
|
|
return less
|
2022-08-29 19:29:24 +08:00
|
|
|
default: // a == true && b == false
|
2022-08-29 19:25:54 +08:00
|
|
|
return more
|
|
|
|
}
|
|
|
|
}
|
2021-12-26 20:18:28 +08:00
|
|
|
}
|
|
|
|
return uncomparable
|
|
|
|
}
|
|
|
|
|
|
|
|
func compareInt(a, b int) ordering {
|
|
|
|
if a < b {
|
|
|
|
return less
|
|
|
|
} else if a > b {
|
|
|
|
return more
|
|
|
|
}
|
|
|
|
return equal
|
|
|
|
}
|
|
|
|
|
|
|
|
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 equal
|
|
|
|
}
|
|
|
|
return less
|
|
|
|
case math.IsNaN(b):
|
|
|
|
return more
|
|
|
|
case a < b:
|
|
|
|
return less
|
|
|
|
case a > b:
|
|
|
|
return more
|
|
|
|
default: // a == b
|
|
|
|
return equal
|
|
|
|
}
|
|
|
|
}
|