github.com/markusbkk/elvish@v0.0.0-20231204143114-91dc52438621/pkg/eval/builtin_fn_pred.go (about) 1 package eval 2 3 import ( 4 "math" 5 "math/big" 6 7 "github.com/markusbkk/elvish/pkg/eval/errs" 8 "github.com/markusbkk/elvish/pkg/eval/vals" 9 ) 10 11 // Basic predicate commands. 12 13 //elvdoc:fn bool 14 // 15 // ```elvish 16 // bool $value 17 // ``` 18 // 19 // Convert a value to boolean. In Elvish, only `$false` and errors are booleanly 20 // false. Everything else, including 0, empty strings and empty lists, is booleanly 21 // true: 22 // 23 // ```elvish-transcript 24 // ~> bool $true 25 // ▶ $true 26 // ~> bool $false 27 // ▶ $false 28 // ~> bool $ok 29 // ▶ $true 30 // ~> bool ?(fail haha) 31 // ▶ $false 32 // ~> bool '' 33 // ▶ $true 34 // ~> bool [] 35 // ▶ $true 36 // ~> bool abc 37 // ▶ $true 38 // ``` 39 // 40 // @cf not 41 42 func init() { 43 addBuiltinFns(map[string]interface{}{ 44 "bool": vals.Bool, 45 "not": not, 46 "is": is, 47 "eq": eq, 48 "not-eq": notEq, 49 "compare": compare, 50 }) 51 } 52 53 //elvdoc:fn not 54 // 55 // ```elvish 56 // not $value 57 // ``` 58 // 59 // Boolean negation. Examples: 60 // 61 // ```elvish-transcript 62 // ~> not $true 63 // ▶ $false 64 // ~> not $false 65 // ▶ $true 66 // ~> not $ok 67 // ▶ $false 68 // ~> not ?(fail error) 69 // ▶ $true 70 // ``` 71 // 72 // **Note**: The related logical commands `and` and `or` are implemented as 73 // [special commands](language.html#special-commands) instead, since they do not 74 // always evaluate all their arguments. The `not` command always evaluates its 75 // only argument, and is thus a normal command. 76 // 77 // @cf bool 78 79 func not(v interface{}) bool { 80 return !vals.Bool(v) 81 } 82 83 //elvdoc:fn is 84 // 85 // ```elvish 86 // is $values... 87 // ``` 88 // 89 // Determine whether all `$value`s have the same identity. Writes `$true` when 90 // given no or one argument. 91 // 92 // The definition of identity is subject to change. Do not rely on its behavior. 93 // 94 // ```elvish-transcript 95 // ~> is a a 96 // ▶ $true 97 // ~> is a b 98 // ▶ $false 99 // ~> is [] [] 100 // ▶ $true 101 // ~> is [a] [a] 102 // ▶ $false 103 // ``` 104 // 105 // @cf eq 106 // 107 // Etymology: [Python](https://docs.python.org/3/reference/expressions.html#is). 108 109 func is(args ...interface{}) bool { 110 for i := 0; i+1 < len(args); i++ { 111 if args[i] != args[i+1] { 112 return false 113 } 114 } 115 return true 116 } 117 118 //elvdoc:fn eq 119 // 120 // ```elvish 121 // eq $values... 122 // ``` 123 // 124 // Determines whether all `$value`s are equal. Writes `$true` when 125 // given no or one argument. 126 // 127 // Two values are equal when they have the same type and value. 128 // 129 // For complex data structures like lists and maps, comparison is done 130 // recursively. A pseudo-map is equal to another pseudo-map with the same 131 // internal type (which is not exposed to Elvish code now) and value. 132 // 133 // ```elvish-transcript 134 // ~> eq a a 135 // ▶ $true 136 // ~> eq [a] [a] 137 // ▶ $true 138 // ~> eq [&k=v] [&k=v] 139 // ▶ $true 140 // ~> eq a [b] 141 // ▶ $false 142 // ``` 143 // 144 // @cf is not-eq 145 // 146 // Etymology: [Perl](https://perldoc.perl.org/perlop.html#Equality-Operators). 147 148 func eq(args ...interface{}) bool { 149 for i := 0; i+1 < len(args); i++ { 150 if !vals.Equal(args[i], args[i+1]) { 151 return false 152 } 153 } 154 return true 155 } 156 157 //elvdoc:fn not-eq 158 // 159 // ```elvish 160 // not-eq $values... 161 // ``` 162 // 163 // Determines whether every adjacent pair of `$value`s are not equal. Note that 164 // this does not imply that `$value`s are all distinct. Examples: 165 // 166 // ```elvish-transcript 167 // ~> not-eq 1 2 3 168 // ▶ $true 169 // ~> not-eq 1 2 1 170 // ▶ $true 171 // ~> not-eq 1 1 2 172 // ▶ $false 173 // ``` 174 // 175 // @cf eq 176 177 func notEq(args ...interface{}) bool { 178 for i := 0; i+1 < len(args); i++ { 179 if vals.Equal(args[i], args[i+1]) { 180 return false 181 } 182 } 183 return true 184 } 185 186 //elvdoc:fn compare 187 // 188 // ```elvish 189 // compare $a $b 190 // ``` 191 // 192 // Outputs -1 if `$a` < `$b`, 0 if `$a` = `$b`, and 1 if `$a` > `$b`. 193 // 194 // The following comparison algorithm is used: 195 // 196 // - Typed numbers are compared numerically. The comparison is consistent with 197 // the [number comparison commands](#num-cmp), except that `NaN` values are 198 // considered equal to each other and smaller than all other numbers. 199 // 200 // - Strings are compared lexicographically by bytes, consistent with the 201 // [string comparison commands](#str-cmp). For UTF-8 encoded strings, this is 202 // equivalent to comparing by codepoints. 203 // 204 // - Lists are compared lexicographically by elements, if the elements at the 205 // same positions are comparable. 206 // 207 // If the ordering between two elements is not defined by the conditions above, 208 // i.e. if the value of `$a` or `$b` is not covered by any of the cases above or 209 // if they belong to different cases, a "bad value" exception is thrown. 210 // 211 // Examples: 212 // 213 // ```elvish-transcript 214 // ~> compare a b 215 // ▶ (num 1) 216 // ~> compare b a 217 // ▶ (num -1) 218 // ~> compare x x 219 // ▶ (num 0) 220 // ~> compare (float64 10) (float64 1) 221 // ▶ (num 1) 222 // ``` 223 // 224 // Beware that strings that look like numbers are treated as strings, not 225 // numbers. 226 // 227 // @cf order 228 229 // ErrUncomparable is raised by the compare and order commands when inputs contain 230 // uncomparable values. 231 var ErrUncomparable = errs.BadValue{ 232 What: `inputs to "compare" or "order"`, 233 Valid: "comparable values", Actual: "uncomparable values"} 234 235 func compare(fm *Frame, a, b interface{}) (int, error) { 236 switch cmp(a, b) { 237 case less: 238 return -1, nil 239 case equal: 240 return 0, nil 241 case more: 242 return 1, nil 243 default: 244 return 0, ErrUncomparable 245 } 246 } 247 248 type ordering uint8 249 250 const ( 251 less ordering = iota 252 equal 253 more 254 uncomparable 255 ) 256 257 func cmp(a, b interface{}) ordering { 258 switch a := a.(type) { 259 case int, *big.Int, *big.Rat, float64: 260 switch b.(type) { 261 case int, *big.Int, *big.Rat, float64: 262 a, b := vals.UnifyNums2(a, b, 0) 263 switch a := a.(type) { 264 case int: 265 return compareInt(a, b.(int)) 266 case *big.Int: 267 return compareInt(a.Cmp(b.(*big.Int)), 0) 268 case *big.Rat: 269 return compareInt(a.Cmp(b.(*big.Rat)), 0) 270 case float64: 271 return compareFloat(a, b.(float64)) 272 default: 273 panic("unreachable") 274 } 275 } 276 case string: 277 if b, ok := b.(string); ok { 278 switch { 279 case a == b: 280 return equal 281 case a < b: 282 return less 283 default: // a > b 284 return more 285 } 286 } 287 case vals.List: 288 if b, ok := b.(vals.List); ok { 289 aIt := a.Iterator() 290 bIt := b.Iterator() 291 for aIt.HasElem() && bIt.HasElem() { 292 o := cmp(aIt.Elem(), bIt.Elem()) 293 if o != equal { 294 return o 295 } 296 aIt.Next() 297 bIt.Next() 298 } 299 switch { 300 case a.Len() == b.Len(): 301 return equal 302 case a.Len() < b.Len(): 303 return less 304 default: // a.Len() > b.Len() 305 return more 306 } 307 } 308 } 309 return uncomparable 310 } 311 312 func compareInt(a, b int) ordering { 313 if a < b { 314 return less 315 } else if a > b { 316 return more 317 } 318 return equal 319 } 320 321 func compareFloat(a, b float64) ordering { 322 // For the sake of ordering, NaN's are considered equal to each 323 // other and smaller than all numbers 324 switch { 325 case math.IsNaN(a): 326 if math.IsNaN(b) { 327 return equal 328 } 329 return less 330 case math.IsNaN(b): 331 return more 332 case a < b: 333 return less 334 case a > b: 335 return more 336 default: // a == b 337 return equal 338 } 339 }