builtin_fn_pred.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. package eval
  2. import (
  3. "math"
  4. "math/big"
  5. "src.elv.sh/pkg/eval/errs"
  6. "src.elv.sh/pkg/eval/vals"
  7. )
  8. // Basic predicate commands.
  9. func init() {
  10. addBuiltinFns(map[string]any{
  11. "bool": vals.Bool,
  12. "not": not,
  13. "is": is,
  14. "eq": eq,
  15. "not-eq": notEq,
  16. "compare": compare,
  17. })
  18. }
  19. func not(v any) bool {
  20. return !vals.Bool(v)
  21. }
  22. func is(args ...any) bool {
  23. for i := 0; i+1 < len(args); i++ {
  24. if args[i] != args[i+1] {
  25. return false
  26. }
  27. }
  28. return true
  29. }
  30. func eq(args ...any) bool {
  31. for i := 0; i+1 < len(args); i++ {
  32. if !vals.Equal(args[i], args[i+1]) {
  33. return false
  34. }
  35. }
  36. return true
  37. }
  38. func notEq(args ...any) bool {
  39. for i := 0; i+1 < len(args); i++ {
  40. if vals.Equal(args[i], args[i+1]) {
  41. return false
  42. }
  43. }
  44. return true
  45. }
  46. // ErrUncomparable is raised by the compare and order commands when inputs contain
  47. // uncomparable values.
  48. var ErrUncomparable = errs.BadValue{
  49. What: `inputs to "compare" or "order"`,
  50. Valid: "comparable values", Actual: "uncomparable values"}
  51. func compare(fm *Frame, a, b any) (int, error) {
  52. switch cmp(a, b) {
  53. case less:
  54. return -1, nil
  55. case equal:
  56. return 0, nil
  57. case more:
  58. return 1, nil
  59. default:
  60. return 0, ErrUncomparable
  61. }
  62. }
  63. type ordering uint8
  64. const (
  65. less ordering = iota
  66. equal
  67. more
  68. uncomparable
  69. )
  70. func cmp(a, b any) ordering {
  71. switch a := a.(type) {
  72. case int, *big.Int, *big.Rat, float64:
  73. switch b.(type) {
  74. case int, *big.Int, *big.Rat, float64:
  75. a, b := vals.UnifyNums2(a, b, 0)
  76. switch a := a.(type) {
  77. case int:
  78. return compareInt(a, b.(int))
  79. case *big.Int:
  80. return compareInt(a.Cmp(b.(*big.Int)), 0)
  81. case *big.Rat:
  82. return compareInt(a.Cmp(b.(*big.Rat)), 0)
  83. case float64:
  84. return compareFloat(a, b.(float64))
  85. default:
  86. panic("unreachable")
  87. }
  88. }
  89. case string:
  90. if b, ok := b.(string); ok {
  91. switch {
  92. case a == b:
  93. return equal
  94. case a < b:
  95. return less
  96. default: // a > b
  97. return more
  98. }
  99. }
  100. case vals.List:
  101. if b, ok := b.(vals.List); ok {
  102. aIt := a.Iterator()
  103. bIt := b.Iterator()
  104. for aIt.HasElem() && bIt.HasElem() {
  105. o := cmp(aIt.Elem(), bIt.Elem())
  106. if o != equal {
  107. return o
  108. }
  109. aIt.Next()
  110. bIt.Next()
  111. }
  112. switch {
  113. case a.Len() == b.Len():
  114. return equal
  115. case a.Len() < b.Len():
  116. return less
  117. default: // a.Len() > b.Len()
  118. return more
  119. }
  120. }
  121. case bool:
  122. if b, ok := b.(bool); ok {
  123. switch {
  124. case a == b:
  125. return equal
  126. //lint:ignore S1002 using booleans as values, not conditions
  127. case a == false: // b == true is implicit
  128. return less
  129. default: // a == true && b == false
  130. return more
  131. }
  132. }
  133. }
  134. return uncomparable
  135. }
  136. func compareInt(a, b int) ordering {
  137. if a < b {
  138. return less
  139. } else if a > b {
  140. return more
  141. }
  142. return equal
  143. }
  144. func compareFloat(a, b float64) ordering {
  145. // For the sake of ordering, NaN's are considered equal to each
  146. // other and smaller than all numbers
  147. switch {
  148. case math.IsNaN(a):
  149. if math.IsNaN(b) {
  150. return equal
  151. }
  152. return less
  153. case math.IsNaN(b):
  154. return more
  155. case a < b:
  156. return less
  157. case a > b:
  158. return more
  159. default: // a == b
  160. return equal
  161. }
  162. }