github.com/elves/elvish@v0.15.0/pkg/eval/builtin_fn_container.go (about) 1 package eval 2 3 import ( 4 "errors" 5 "fmt" 6 "math" 7 "sort" 8 9 "github.com/elves/elvish/pkg/eval/errs" 10 "github.com/elves/elvish/pkg/eval/vals" 11 "github.com/elves/elvish/pkg/eval/vars" 12 "github.com/xiaq/persistent/hashmap" 13 ) 14 15 // Sequence, list and maps. 16 17 func init() { 18 addBuiltinFns(map[string]interface{}{ 19 "ns": nsFn, 20 21 "make-map": makeMap, 22 23 "range": rangeFn, 24 "repeat": repeat, 25 26 "assoc": assoc, 27 "dissoc": dissoc, 28 29 "all": all, 30 "one": one, 31 32 "has-key": hasKey, 33 "has-value": hasValue, 34 35 "take": take, 36 "drop": drop, 37 "count": count, 38 39 "keys": keys, 40 41 "order": order, 42 }) 43 } 44 45 //elvdoc:fn ns 46 // 47 // ```elvish 48 // ns $map 49 // ``` 50 // 51 // Constructs a namespace from `$map`, using the keys as variable names and the 52 // values as their values. Examples: 53 // 54 // ```elvish-transcript 55 // ~> n = (ns [&name=value]) 56 // ~> put $n[name] 57 // ▶ value 58 // ~> n: = (ns [&name=value]) 59 // ~> put $n:name 60 // ▶ value 61 // ``` 62 63 func nsFn(m hashmap.Map) (*Ns, error) { 64 nb := make(NsBuilder) 65 for it := m.Iterator(); it.HasElem(); it.Next() { 66 k, v := it.Elem() 67 kstring, ok := k.(string) 68 if !ok { 69 return nil, errs.BadValue{ 70 What: `key of argument of "ns"`, 71 Valid: "string", Actual: vals.Kind(k)} 72 } 73 nb[kstring] = vars.FromInit(v) 74 } 75 return nb.Ns(), nil 76 } 77 78 //elvdoc:fn make-map 79 // 80 // ```elvish 81 // make-map $input? 82 // ``` 83 // 84 // Outputs a map from an input consisting of containers with two elements. The 85 // first element of each container is used as the key, and the second element is 86 // used as the value. 87 // 88 // If the same key appears multiple times, the last value is used. 89 // 90 // Examples: 91 // 92 // ```elvish-transcript 93 // ~> make-map [[k v]] 94 // ▶ [&k=v] 95 // ~> make-map [[k v1] [k v2]] 96 // ▶ [&k=v2] 97 // ~> put [k1 v1] [k2 v2] | make-map 98 // ▶ [&k1=v1 &k2=v2] 99 // ~> put aA bB | make-map 100 // ▶ [&a=A &b=B] 101 // ``` 102 103 func makeMap(input Inputs) (vals.Map, error) { 104 m := vals.EmptyMap 105 var errMakeMap error 106 input(func(v interface{}) { 107 if errMakeMap != nil { 108 return 109 } 110 if !vals.CanIterate(v) { 111 errMakeMap = errs.BadValue{ 112 What: "input to make-map", Valid: "iterable", Actual: vals.Kind(v)} 113 return 114 } 115 if l := vals.Len(v); l != 2 { 116 errMakeMap = errs.BadValue{ 117 What: "input to make-map", Valid: "iterable with 2 elements", 118 Actual: fmt.Sprintf("%v with %v elements", vals.Kind(v), l)} 119 return 120 } 121 elems, err := vals.Collect(v) 122 if err != nil { 123 errMakeMap = err 124 return 125 } 126 if len(elems) != 2 { 127 errMakeMap = fmt.Errorf("internal bug: collected %v values", len(elems)) 128 return 129 } 130 m = m.Assoc(elems[0], elems[1]) 131 }) 132 return m, errMakeMap 133 } 134 135 //elvdoc:fn range 136 // 137 // ```elvish 138 // range &step=1 $low? $high 139 // ``` 140 // 141 // Output `$low`, `$low` + `$step`, ..., proceeding as long as smaller than 142 // `$high`. If not given, `$low` defaults to 0. 143 // 144 // Examples: 145 // 146 // ```elvish-transcript 147 // ~> range 4 148 // ▶ 0 149 // ▶ 1 150 // ▶ 2 151 // ▶ 3 152 // ~> range 1 6 &step=2 153 // ▶ 1 154 // ▶ 3 155 // ▶ 5 156 // ``` 157 // 158 // Beware floating point oddities: 159 // 160 // ```elvish-transcript 161 // ~> range 0 0.8 &step=.1 162 // ▶ 0 163 // ▶ 0.1 164 // ▶ 0.2 165 // ▶ 0.30000000000000004 166 // ▶ 0.4 167 // ▶ 0.5 168 // ▶ 0.6 169 // ▶ 0.7 170 // ▶ 0.7999999999999999 171 // ``` 172 // 173 // Etymology: 174 // [Python](https://docs.python.org/3/library/functions.html#func-range). 175 176 type rangeOpts struct{ Step float64 } 177 178 func (o *rangeOpts) SetDefaultOptions() { o.Step = 1 } 179 180 func rangeFn(fm *Frame, opts rangeOpts, args ...float64) error { 181 var lower, upper float64 182 183 switch len(args) { 184 case 1: 185 upper = args[0] 186 case 2: 187 lower, upper = args[0], args[1] 188 default: 189 return ErrArgs 190 } 191 192 out := fm.OutputChan() 193 for f := lower; f < upper; f += opts.Step { 194 out <- vals.FromGo(f) 195 } 196 return nil 197 } 198 199 //elvdoc:fn repeat 200 // 201 // ```elvish 202 // repeat $n $value 203 // ``` 204 // 205 // Output `$value` for `$n` times. Example: 206 // 207 // ```elvish-transcript 208 // ~> repeat 0 lorem 209 // ~> repeat 4 NAN 210 // ▶ NAN 211 // ▶ NAN 212 // ▶ NAN 213 // ▶ NAN 214 // ``` 215 // 216 // Etymology: [Clojure](https://clojuredocs.org/clojure.core/repeat). 217 218 func repeat(fm *Frame, n int, v interface{}) { 219 out := fm.OutputChan() 220 for i := 0; i < n; i++ { 221 out <- v 222 } 223 } 224 225 //elvdoc:fn assoc 226 // 227 // ```elvish 228 // assoc $container $k $v 229 // ``` 230 // 231 // Output a slightly modified version of `$container`, such that its value at `$k` 232 // is `$v`. Applies to both lists and to maps. 233 // 234 // When `$container` is a list, `$k` may be a negative index. However, slice is not 235 // yet supported. 236 // 237 // ```elvish-transcript 238 // ~> assoc [foo bar quux] 0 lorem 239 // ▶ [lorem bar quux] 240 // ~> assoc [foo bar quux] -1 ipsum 241 // ▶ [foo bar ipsum] 242 // ~> assoc [&k=v] k v2 243 // ▶ [&k=v2] 244 // ~> assoc [&k=v] k2 v2 245 // ▶ [&k2=v2 &k=v] 246 // ``` 247 // 248 // Etymology: [Clojure](https://clojuredocs.org/clojure.core/assoc). 249 // 250 // @cf dissoc 251 252 func assoc(a, k, v interface{}) (interface{}, error) { 253 return vals.Assoc(a, k, v) 254 } 255 256 var errCannotDissoc = errors.New("cannot dissoc") 257 258 //elvdoc:fn dissoc 259 // 260 // ```elvish 261 // dissoc $map $k 262 // ``` 263 // 264 // Output a slightly modified version of `$map`, with the key `$k` removed. If 265 // `$map` does not contain `$k` as a key, the same map is returned. 266 // 267 // ```elvish-transcript 268 // ~> dissoc [&foo=bar &lorem=ipsum] foo 269 // ▶ [&lorem=ipsum] 270 // ~> dissoc [&foo=bar &lorem=ipsum] k 271 // ▶ [&lorem=ipsum &foo=bar] 272 // ``` 273 // 274 // @cf assoc 275 276 func dissoc(a, k interface{}) (interface{}, error) { 277 a2 := vals.Dissoc(a, k) 278 if a2 == nil { 279 return nil, errCannotDissoc 280 } 281 return a2, nil 282 } 283 284 //elvdoc:fn all 285 // 286 // ```elvish 287 // all $input-list? 288 // ``` 289 // 290 // Passes inputs to the output as is. Byte inputs into values, one per line. 291 // 292 // This is an identity function for commands with value outputs: `a | all` is 293 // equivalent to `a` if it only outputs values. 294 // 295 // This function is useful for turning inputs into arguments, like: 296 // 297 // ```elvish-transcript 298 // ~> use str 299 // ~> put 'lorem,ipsum' | str:split , (all) 300 // ▶ lorem 301 // ▶ ipsum 302 // ``` 303 // 304 // Or capturing all inputs in a variable: 305 // 306 // ```elvish-transcript 307 // ~> x = [(all)] 308 // foo 309 // bar 310 // (Press ^D) 311 // ~> put $x 312 // ▶ [foo bar] 313 // ``` 314 // 315 // When given a list, it outputs all elements of the list: 316 // 317 // ```elvish-transcript 318 // ~> all [foo bar] 319 // ▶ foo 320 // ▶ bar 321 // ``` 322 // 323 // @cf one 324 325 func all(fm *Frame, inputs Inputs) { 326 out := fm.OutputChan() 327 inputs(func(v interface{}) { out <- v }) 328 } 329 330 //elvdoc:fn one 331 // 332 // ```elvish 333 // one $input-list? 334 // ``` 335 // 336 // Passes inputs to outputs, if there is only a single one. Otherwise raises an 337 // exception. 338 // 339 // This function can be used in a similar way to [`all`](#all), but is a better 340 // choice when you expect that there is exactly one output: 341 // 342 // @cf all 343 344 func one(fm *Frame, inputs Inputs) error { 345 var val interface{} 346 n := 0 347 inputs(func(v interface{}) { 348 if n == 0 { 349 val = v 350 } 351 n++ 352 }) 353 if n == 1 { 354 fm.OutputChan() <- val 355 return nil 356 } 357 return fmt.Errorf("expect a single value, got %d", n) 358 } 359 360 //elvdoc:fn take 361 // 362 // ```elvish 363 // take $n $input-list? 364 // ``` 365 // 366 // Retain the first `$n` input elements. If `$n` is larger than the number of input 367 // elements, the entire input is retained. Examples: 368 // 369 // ```elvish-transcript 370 // ~> take 3 [a b c d e] 371 // ▶ a 372 // ▶ b 373 // ▶ c 374 // ~> use str 375 // ~> str:split ' ' 'how are you?' | take 1 376 // ▶ how 377 // ~> range 2 | take 10 378 // ▶ 0 379 // ▶ 1 380 // ``` 381 // 382 // Etymology: Haskell. 383 384 func take(fm *Frame, n int, inputs Inputs) { 385 out := fm.OutputChan() 386 i := 0 387 inputs(func(v interface{}) { 388 if i < n { 389 out <- v 390 } 391 i++ 392 }) 393 } 394 395 //elvdoc:fn drop 396 // 397 // ```elvish 398 // drop $n $input-list? 399 // ``` 400 // 401 // Drop the first `$n` elements of the input. If `$n` is larger than the number of 402 // input elements, the entire input is dropped. 403 // 404 // Example: 405 // 406 // ```elvish-transcript 407 // ~> drop 2 [a b c d e] 408 // ▶ c 409 // ▶ d 410 // ▶ e 411 // ~> use str 412 // ~> str:split ' ' 'how are you?' | drop 1 413 // ▶ are 414 // ▶ 'you?' 415 // ~> range 2 | drop 10 416 // ``` 417 // 418 // Etymology: Haskell. 419 // 420 // @cf take 421 422 func drop(fm *Frame, n int, inputs Inputs) { 423 out := fm.OutputChan() 424 i := 0 425 inputs(func(v interface{}) { 426 if i >= n { 427 out <- v 428 } 429 i++ 430 }) 431 } 432 433 //elvdoc:fn has-value 434 // 435 // ```elvish 436 // has-value $container $value 437 // ``` 438 // 439 // Determine whether `$value` is a value in `$container`. 440 // 441 // Examples, maps: 442 // 443 // ```elvish-transcript 444 // ~> has-value [&k1=v1 &k2=v2] v1 445 // ▶ $true 446 // ~> has-value [&k1=v1 &k2=v2] k1 447 // ▶ $false 448 // ``` 449 // 450 // Examples, lists: 451 // 452 // ```elvish-transcript 453 // ~> has-value [v1 v2] v1 454 // ▶ $true 455 // ~> has-value [v1 v2] k1 456 // ▶ $false 457 // ``` 458 // 459 // Examples, strings: 460 // 461 // ```elvish-transcript 462 // ~> has-value ab b 463 // ▶ $true 464 // ~> has-value ab c 465 // ▶ $false 466 // ``` 467 468 func hasValue(container, value interface{}) (bool, error) { 469 switch container := container.(type) { 470 case hashmap.Map: 471 for it := container.Iterator(); it.HasElem(); it.Next() { 472 _, v := it.Elem() 473 if vals.Equal(v, value) { 474 return true, nil 475 } 476 } 477 return false, nil 478 default: 479 var found bool 480 err := vals.Iterate(container, func(v interface{}) bool { 481 found = (v == value) 482 return !found 483 }) 484 return found, err 485 } 486 } 487 488 //elvdoc:fn has-key 489 // 490 // ```elvish 491 // has-key $container $key 492 // ``` 493 // 494 // Determine whether `$key` is a key in `$container`. A key could be a map key or 495 // an index on a list or string. This includes a range of indexes. 496 // 497 // Examples, maps: 498 // 499 // ```elvish-transcript 500 // ~> has-key [&k1=v1 &k2=v2] k1 501 // ▶ $true 502 // ~> has-key [&k1=v1 &k2=v2] v1 503 // ▶ $false 504 // ``` 505 // 506 // Examples, lists: 507 // 508 // ```elvish-transcript 509 // ~> has-key [v1 v2] 0 510 // ▶ $true 511 // ~> has-key [v1 v2] 1 512 // ▶ $true 513 // ~> has-key [v1 v2] 2 514 // ▶ $false 515 // ~> has-key [v1 v2] 0:2 516 // ▶ $true 517 // ~> has-key [v1 v2] 0:3 518 // ▶ $false 519 // ``` 520 // 521 // Examples, strings: 522 // 523 // ```elvish-transcript 524 // ~> has-key ab 0 525 // ▶ $true 526 // ~> has-key ab 1 527 // ▶ $true 528 // ~> has-key ab 2 529 // ▶ $false 530 // ~> has-key ab 0:2 531 // ▶ $true 532 // ~> has-key ab 0:3 533 // ▶ $false 534 // ``` 535 536 func hasKey(container, key interface{}) bool { 537 return vals.HasKey(container, key) 538 } 539 540 //elvdoc:fn count 541 // 542 // ```elvish 543 // count $input-list? 544 // ``` 545 // 546 // Count the number of inputs. 547 // 548 // Examples: 549 // 550 // ```elvish-transcript 551 // ~> count lorem # count bytes in a string 552 // ▶ 5 553 // ~> count [lorem ipsum] 554 // ▶ 2 555 // ~> range 100 | count 556 // ▶ 100 557 // ~> seq 100 | count 558 // ▶ 100 559 // ``` 560 561 // The count implementation uses a custom varargs based implementation rather 562 // than the more common `Inputs` API (see pkg/eval/go_fn.go) because this 563 // allows the implementation to be O(1) for the common cases rather than O(n). 564 func count(fm *Frame, args ...interface{}) (int, error) { 565 var n int 566 switch nargs := len(args); nargs { 567 case 0: 568 // Count inputs. 569 fm.IterateInputs(func(interface{}) { 570 n++ 571 }) 572 case 1: 573 // Get length of argument. 574 v := args[0] 575 if len := vals.Len(v); len >= 0 { 576 n = len 577 } else { 578 err := vals.Iterate(v, func(interface{}) bool { 579 n++ 580 return true 581 }) 582 if err != nil { 583 return 0, fmt.Errorf("cannot get length of a %s", vals.Kind(v)) 584 } 585 } 586 default: 587 // The error matches what would be returned if the `Inputs` API was 588 // used. See GoFn.Call(). 589 return 0, errs.ArityMismatch{ 590 What: "arguments here", ValidLow: 0, ValidHigh: 1, Actual: nargs} 591 } 592 return n, nil 593 } 594 595 //elvdoc:fn keys 596 // 597 // ```elvish 598 // keys $map 599 // ``` 600 // 601 // Put all keys of `$map` on the structured stdout. 602 // 603 // Example: 604 // 605 // ```elvish-transcript 606 // ~> keys [&a=foo &b=bar &c=baz] 607 // ▶ a 608 // ▶ c 609 // ▶ b 610 // ``` 611 // 612 // Note that there is no guaranteed order for the keys of a map. 613 614 func keys(fm *Frame, v interface{}) error { 615 out := fm.OutputChan() 616 return vals.IterateKeys(v, func(k interface{}) bool { 617 out <- k 618 return true 619 }) 620 } 621 622 //elvdoc:fn order 623 // 624 // ```elvish 625 // order &reverse=$false $less-than=$nil $inputs? 626 // ``` 627 // 628 // Outputs the input values sorted in ascending order. The sort is guaranteed to 629 // be [stable](https://en.wikipedia.org/wiki/Sorting_algorithm#Stability). 630 // 631 // The `&reverse` option, if true, reverses the order of output. 632 // 633 // The `&less-than` option, if given, establishes the ordering of the elements. 634 // Its value should be a function that takes two arguments and outputs a single 635 // boolean indicating whether the first argument is less than the second 636 // argument. If the function throws an exception, `order` rethrows the exception 637 // without outputting any value. 638 // 639 // If `&less-than` has value `$nil` (the default if not set), the following 640 // comparison algorithm is used: 641 // 642 // - Numbers are compared numerically. For the sake of sorting, `NaN` is treated 643 // as smaller than all other numbers. 644 // 645 // - Strings are compared lexicographically by bytes, which is equivalent to 646 // comparing by codepoints under UTF-8. 647 // 648 // - Lists are compared lexicographically by elements, if the elements at the 649 // same positions are comparable. 650 // 651 // If the ordering between two elements are not defined by the conditions above, 652 // no value is outputted and an exception is thrown. 653 // 654 // Examples: 655 // 656 // ```elvish-transcript 657 // ~> put foo bar ipsum | order 658 // ▶ bar 659 // ▶ foo 660 // ▶ ipsum 661 // ~> order [(float64 10) (float64 1) (float64 5)] 662 // ▶ (float64 1) 663 // ▶ (float64 5) 664 // ▶ (float64 10) 665 // ~> order [[a b] [a] [b b] [a c]] 666 // ▶ [a] 667 // ▶ [a b] 668 // ▶ [a c] 669 // ▶ [b b] 670 // ~> order &reverse [a c b] 671 // ▶ c 672 // ▶ b 673 // ▶ a 674 // ~> order &less-than=[a b]{ eq $a x } [l x o r x e x m] 675 // ▶ x 676 // ▶ x 677 // ▶ x 678 // ▶ l 679 // ▶ o 680 // ▶ r 681 // ▶ e 682 // ▶ m 683 // ``` 684 // 685 // Beware that strings that look like numbers are treated as strings, not 686 // numbers. To sort strings as numbers, use an explicit `&less-than` option: 687 // 688 // ```elvish-transcript 689 // ~> order [5 1 10] 690 // ▶ 1 691 // ▶ 10 692 // ▶ 5 693 // ~> order &less-than=[a b]{ < $a $b } [5 1 10] 694 // ▶ 1 695 // ▶ 5 696 // ▶ 10 697 // ``` 698 699 type orderOptions struct { 700 Reverse bool 701 LessThan Callable 702 } 703 704 func (opt *orderOptions) SetDefaultOptions() {} 705 706 // ErrUncomparable is raised by the order command when inputs contain 707 // uncomparable values. 708 var ErrUncomparable = errs.BadValue{ 709 What: `inputs to "order"`, 710 Valid: "comparable values", Actual: "uncomparable values"} 711 712 func order(fm *Frame, opts orderOptions, inputs Inputs) error { 713 var values []interface{} 714 inputs(func(v interface{}) { values = append(values, v) }) 715 716 var errSort error 717 var lessFn func(i, j int) bool 718 if opts.LessThan != nil { 719 lessFn = func(i, j int) bool { 720 if errSort != nil { 721 return true 722 } 723 var args []interface{} 724 if opts.Reverse { 725 args = []interface{}{values[j], values[i]} 726 } else { 727 args = []interface{}{values[i], values[j]} 728 } 729 outputs, err := fm.CaptureOutput(func(fm *Frame) error { 730 return opts.LessThan.Call(fm, args, NoOpts) 731 }) 732 if err != nil { 733 errSort = err 734 return true 735 } 736 if len(outputs) != 1 { 737 errSort = errs.BadValue{ 738 What: "output of the &less-than callback", 739 Valid: "a single boolean", 740 Actual: fmt.Sprintf("%d values", len(outputs))} 741 return true 742 } 743 if b, ok := outputs[0].(bool); ok { 744 return b 745 } 746 errSort = errs.BadValue{ 747 What: "output of the &less-than callback", 748 Valid: "boolean", Actual: vals.Kind(outputs[0])} 749 return true 750 } 751 } else { 752 // Use default comparison implemented by compare. 753 lessFn = func(i, j int) bool { 754 if errSort != nil { 755 return true 756 } 757 o := compare(values[i], values[j]) 758 if o == uncomparable { 759 errSort = ErrUncomparable 760 return true 761 } 762 if opts.Reverse { 763 return o == more 764 } 765 return o == less 766 } 767 } 768 769 sort.SliceStable(values, lessFn) 770 771 if errSort != nil { 772 return errSort 773 } 774 for _, v := range values { 775 fm.OutputChan() <- v 776 } 777 return nil 778 } 779 780 type ordering uint8 781 782 const ( 783 less ordering = iota 784 equal 785 more 786 uncomparable 787 ) 788 789 func compare(a, b interface{}) ordering { 790 switch a := a.(type) { 791 case float64: 792 if b, ok := b.(float64); ok { 793 switch { 794 case math.IsNaN(a): 795 if math.IsNaN(b) { 796 return equal 797 } 798 return less 799 case math.IsNaN(b): 800 return more 801 case a == b: 802 return equal 803 case a < b: 804 return less 805 default: 806 // a > b 807 return more 808 } 809 } 810 case string: 811 if b, ok := b.(string); ok { 812 switch { 813 case a == b: 814 return equal 815 case a < b: 816 return less 817 default: 818 // a > b 819 return more 820 } 821 } 822 case vals.List: 823 if b, ok := b.(vals.List); ok { 824 aIt := a.Iterator() 825 bIt := b.Iterator() 826 for aIt.HasElem() && bIt.HasElem() { 827 o := compare(aIt.Elem(), bIt.Elem()) 828 if o != equal { 829 return o 830 } 831 aIt.Next() 832 bIt.Next() 833 } 834 switch { 835 case a.Len() == b.Len(): 836 return equal 837 case a.Len() < b.Len(): 838 return less 839 default: 840 // a.Len() > b.Len() 841 return more 842 } 843 } 844 } 845 return uncomparable 846 }