builtin_fn_stream.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572
  1. package eval
  2. import (
  3. "errors"
  4. "fmt"
  5. "sort"
  6. "src.elv.sh/pkg/eval/errs"
  7. "src.elv.sh/pkg/eval/vals"
  8. )
  9. // Stream manipulation.
  10. func init() {
  11. addBuiltinFns(map[string]any{
  12. "all": all,
  13. "one": one,
  14. "take": take,
  15. "drop": drop,
  16. "compact": compact,
  17. "count": count,
  18. "order": order,
  19. })
  20. }
  21. var ErrKeyAndLessThanOpts = errors.New("options &key and &less-than are mutually exclusive")
  22. //elvdoc:fn all
  23. //
  24. // ```elvish
  25. // all $inputs?
  26. // ```
  27. //
  28. // Takes [value inputs](#value-inputs), and outputs those values unchanged.
  29. //
  30. // This is an [identity
  31. // function](https://en.wikipedia.org/wiki/Identity_function) for the value
  32. // channel; in other words, `a | all` is equivalent to just `a` if `a` only has
  33. // value output.
  34. //
  35. // This command can be used inside output capture (i.e. `(all)`) to turn value
  36. // inputs into arguments. For example:
  37. //
  38. // ```elvish-transcript
  39. // ~> echo '["foo","bar"] ["lorem","ipsum"]' | from-json
  40. // ▶ [foo bar]
  41. // ▶ [lorem ipsum]
  42. // ~> echo '["foo","bar"] ["lorem","ipsum"]' | from-json | put (all)[0]
  43. // ▶ foo
  44. // ▶ lorem
  45. // ```
  46. //
  47. // The latter pipeline is equivalent to the following:
  48. //
  49. // ```elvish-transcript
  50. // ~> put (echo '["foo","bar"] ["lorem","ipsum"]' | from-json)[0]
  51. // ▶ foo
  52. // ▶ lorem
  53. // ```
  54. //
  55. // In general, when `(all)` appears in the last command of a pipeline, it is
  56. // equivalent to just moving the previous commands of the pipeline into `()`.
  57. // The choice is a stylistic one; the `(all)` variant is longer overall, but can
  58. // be more readable since it allows you to avoid putting an excessively long
  59. // pipeline inside an output capture, and keeps the data flow within the
  60. // pipeline.
  61. //
  62. // Putting the value capture inside `[]` (i.e. `[(all)]`) is useful for storing
  63. // all value inputs in a list for further processing:
  64. //
  65. // ```elvish-transcript
  66. // ~> fn f { var inputs = [(all)]; put $inputs[1] }
  67. // ~> put foo bar baz | f
  68. // ▶ bar
  69. // ```
  70. //
  71. // The `all` command can also take "inputs" from an iterable argument. This can
  72. // be used to flatten lists or strings (although not recursively):
  73. //
  74. // ```elvish-transcript
  75. // ~> all [foo [lorem ipsum]]
  76. // ▶ foo
  77. // ▶ [lorem ipsum]
  78. // ~> all foo
  79. // ▶ f
  80. // ▶ o
  81. // ▶ o
  82. // ```
  83. //
  84. // This can be used together with `(one)` to turn a single iterable value in the
  85. // pipeline into its elements:
  86. //
  87. // ```elvish-transcript
  88. // ~> echo '["foo","bar","lorem"]' | from-json
  89. // ▶ [foo bar lorem]
  90. // ~> echo '["foo","bar","lorem"]' | from-json | all (one)
  91. // ▶ foo
  92. // ▶ bar
  93. // ▶ lorem
  94. // ```
  95. //
  96. // When given byte inputs, the `all` command currently functions like
  97. // [`from-lines`](#from-lines), although this behavior is subject to change:
  98. //
  99. // ```elvish-transcript
  100. // ~> print "foo\nbar\n" | all
  101. // ▶ foo
  102. // ▶ bar
  103. // ```
  104. //
  105. // @cf one
  106. func all(fm *Frame, inputs Inputs) error {
  107. out := fm.ValueOutput()
  108. var errOut error
  109. inputs(func(v any) {
  110. if errOut != nil {
  111. return
  112. }
  113. errOut = out.Put(v)
  114. })
  115. return errOut
  116. }
  117. //elvdoc:fn one
  118. //
  119. // ```elvish
  120. // one $inputs?
  121. // ```
  122. //
  123. // Takes exactly one [value input](#value-inputs) and outputs it. If there are
  124. // more than one value inputs, raises an exception.
  125. //
  126. // This function can be used in a similar way to [`all`](#all), but is a better
  127. // choice when you expect that there is exactly one output.
  128. //
  129. // @cf all
  130. func one(fm *Frame, inputs Inputs) error {
  131. var val any
  132. n := 0
  133. inputs(func(v any) {
  134. if n == 0 {
  135. val = v
  136. }
  137. n++
  138. })
  139. if n == 1 {
  140. return fm.ValueOutput().Put(val)
  141. }
  142. return errs.ArityMismatch{What: "values", ValidLow: 1, ValidHigh: 1, Actual: n}
  143. }
  144. //elvdoc:fn take
  145. //
  146. // ```elvish
  147. // take $n $inputs?
  148. // ```
  149. //
  150. // Outputs the first `$n` [value inputs](#value-inputs). If `$n` is larger than
  151. // the number of value inputs, outputs everything.
  152. //
  153. // Examples:
  154. //
  155. // ```elvish-transcript
  156. // ~> range 2 | take 10
  157. // ▶ 0
  158. // ▶ 1
  159. // ~> take 3 [a b c d e]
  160. // ▶ a
  161. // ▶ b
  162. // ▶ c
  163. // ~> use str
  164. // ~> str:split ' ' 'how are you?' | take 1
  165. // ▶ how
  166. // ```
  167. //
  168. // Etymology: Haskell.
  169. //
  170. // @cf drop
  171. func take(fm *Frame, n int, inputs Inputs) error {
  172. out := fm.ValueOutput()
  173. var errOut error
  174. i := 0
  175. inputs(func(v any) {
  176. if errOut != nil {
  177. return
  178. }
  179. if i < n {
  180. errOut = out.Put(v)
  181. }
  182. i++
  183. })
  184. return errOut
  185. }
  186. //elvdoc:fn drop
  187. //
  188. // ```elvish
  189. // drop $n $inputs?
  190. // ```
  191. //
  192. // Ignores the first `$n` [value inputs](#value-inputs) and outputs the rest.
  193. // If `$n` is larger than the number of value inputs, outputs nothing.
  194. //
  195. // Example:
  196. //
  197. // ```elvish-transcript
  198. // ~> range 10 | drop 8
  199. // ▶ (num 8)
  200. // ▶ (num 9)
  201. // ~> range 2 | drop 10
  202. // ~> drop 2 [a b c d e]
  203. // ▶ c
  204. // ▶ d
  205. // ▶ e
  206. // ~> use str
  207. // ~> str:split ' ' 'how are you?' | drop 1
  208. // ▶ are
  209. // ▶ 'you?'
  210. // ```
  211. //
  212. // Etymology: Haskell.
  213. //
  214. // @cf take
  215. func drop(fm *Frame, n int, inputs Inputs) error {
  216. out := fm.ValueOutput()
  217. var errOut error
  218. i := 0
  219. inputs(func(v any) {
  220. if errOut != nil {
  221. return
  222. }
  223. if i >= n {
  224. errOut = out.Put(v)
  225. }
  226. i++
  227. })
  228. return errOut
  229. }
  230. //elvdoc:fn compact
  231. //
  232. // ```elvish
  233. // compact $inputs?
  234. // ```
  235. //
  236. // Replaces consecutive runs of equal values with a single copy. Similar to the
  237. // `uniq` command on Unix.
  238. //
  239. // Examples:
  240. //
  241. // ```elvish-transcript
  242. // ~> put a a b b c | compact
  243. // ▶ a
  244. // ▶ b
  245. // ▶ c
  246. // ~> compact [a a b b c]
  247. // ▶ a
  248. // ▶ b
  249. // ▶ c
  250. // ~> put a b a | compact
  251. // ▶ a
  252. // ▶ b
  253. // ▶ a
  254. // ```
  255. func compact(fm *Frame, inputs Inputs) error {
  256. out := fm.ValueOutput()
  257. first := true
  258. var errOut error
  259. var prev any
  260. inputs(func(v any) {
  261. if errOut != nil {
  262. return
  263. }
  264. if first || !vals.Equal(v, prev) {
  265. errOut = out.Put(v)
  266. first = false
  267. prev = v
  268. }
  269. })
  270. return errOut
  271. }
  272. //elvdoc:fn count
  273. //
  274. // ```elvish
  275. // count $input-list?
  276. // ```
  277. //
  278. // Count the number of inputs.
  279. //
  280. // Examples:
  281. //
  282. // ```elvish-transcript
  283. // ~> count lorem # count bytes in a string
  284. // ▶ 5
  285. // ~> count [lorem ipsum]
  286. // ▶ 2
  287. // ~> range 100 | count
  288. // ▶ 100
  289. // ~> seq 100 | count
  290. // ▶ 100
  291. // ```
  292. // The count implementation uses a custom varargs based implementation rather
  293. // than the more common `Inputs` API (see pkg/eval/go_fn.go) because this
  294. // allows the implementation to be O(1) for the common cases rather than O(n).
  295. func count(fm *Frame, args ...any) (int, error) {
  296. var n int
  297. switch nargs := len(args); nargs {
  298. case 0:
  299. // Count inputs.
  300. fm.IterateInputs(func(any) {
  301. n++
  302. })
  303. case 1:
  304. // Get length of argument.
  305. v := args[0]
  306. if len := vals.Len(v); len >= 0 {
  307. n = len
  308. } else {
  309. err := vals.Iterate(v, func(any) bool {
  310. n++
  311. return true
  312. })
  313. if err != nil {
  314. return 0, fmt.Errorf("cannot get length of a %s", vals.Kind(v))
  315. }
  316. }
  317. default:
  318. // The error matches what would be returned if the `Inputs` API was
  319. // used. See GoFn.Call().
  320. return 0, errs.ArityMismatch{What: "arguments", ValidLow: 0, ValidHigh: 1, Actual: nargs}
  321. }
  322. return n, nil
  323. }
  324. //elvdoc:fn order
  325. //
  326. // ```elvish
  327. // order &reverse=$false &key=$nil &less-than=$nil $inputs?
  328. // ```
  329. //
  330. // Outputs the [value inputs](#value-inputs) sorted in ascending order (ignoring
  331. // the behavior of any `&less-than` callable). The sorting process is guaranteed
  332. // to be [stable](https://en.wikipedia.org/wiki/Sorting_algorithm#Stability).
  333. //
  334. // The `&reverse` option, if true, reverses the order of output; e.g.,
  335. // descending rather than ascending.
  336. //
  337. // The `&key` option, if not `$nil` (the default value), is a function that
  338. // accepts an item to be sorted (string, list, map, etc.) and outputs a single
  339. // value (which could be a list or map) that is passed to the default
  340. // comparison function. If the key function throws an exception, `order`
  341. // rethrows the exception.
  342. //
  343. // The `&less-than` option, if not `$nil` (the default value), establishes the
  344. // ordering of the items. Its value should be a function that takes two
  345. // arguments and outputs `$true` if the first argument is less than the second
  346. // argument. If the function throws an exception, `order` rethrows the exception
  347. // without outputting any value. If `&less-than` has value `$nil` (the default
  348. // if not set), it is equivalent to `{|a b| == -1 (compare $a $b) }`.
  349. //
  350. // You can use `&key` or `&less-than` but not both at the same time. A `&key`
  351. // function is usually preferable to a `&less-than` function because it is more
  352. // efficient extract the key for each item just once rather than on each
  353. // comparison. If both options are `$nil` then the entirety of each item is
  354. // compared using the equivalent of `{|a b| == -1 (compare $a $b) }`.
  355. //
  356. // Examples:
  357. //
  358. // ```elvish-transcript
  359. // ~> put foo bar ipsum | order
  360. // ▶ bar
  361. // ▶ foo
  362. // ▶ ipsum
  363. // ~> order [(num 10) (num 1) (num 5)]
  364. // ▶ (num 1)
  365. // ▶ (num 5)
  366. // ▶ (num 10)
  367. // ~> order [[a b] [a] [b b] [a c]]
  368. // ▶ [a]
  369. // ▶ [a b]
  370. // ▶ [a c]
  371. // ▶ [b b]
  372. // ~> order &reverse [a c b]
  373. // ▶ c
  374. // ▶ b
  375. // ▶ a
  376. // ~> order &less-than={|a b| eq $a x } [l x o r x e x m]
  377. // ▶ x
  378. // ▶ x
  379. // ▶ x
  380. // ▶ l
  381. // ▶ o
  382. // ▶ r
  383. // ▶ e
  384. // ▶ m
  385. // ```
  386. //
  387. // Beware that strings that look like numbers are treated as strings, not
  388. // numbers. To sort strings as numbers, use an explicit `&less-than` option:
  389. //
  390. // ```elvish-transcript
  391. // ~> order [5 1 10]
  392. // ▶ 1
  393. // ▶ 10
  394. // ▶ 5
  395. // ~> order &less-than={|a b| < $a $b } [5 1 10]
  396. // ▶ 1
  397. // ▶ 5
  398. // ▶ 10
  399. // ```
  400. //
  401. // @cf compare
  402. type orderOptions struct {
  403. Reverse bool
  404. Key Callable
  405. LessThan Callable
  406. }
  407. func (opt *orderOptions) SetDefaultOptions() {}
  408. func order(fm *Frame, opts orderOptions, inputs Inputs) error {
  409. if opts.Key != nil && opts.LessThan != nil {
  410. return ErrKeyAndLessThanOpts
  411. }
  412. values, err := collectValues(fm, opts, inputs)
  413. if err != nil {
  414. return err
  415. }
  416. var sortErr error
  417. cmpFn := getCmpFunction(fm, opts, values, &sortErr)
  418. sort.SliceStable(values, cmpFn)
  419. if sortErr != nil {
  420. return sortErr
  421. }
  422. out := fm.ValueOutput()
  423. for _, v := range values {
  424. var err error
  425. if opts.Key == nil {
  426. // No `&key` option was used so simply output the original value.
  427. err = out.Put(v)
  428. } else {
  429. // Ignore the key generated by the `&key` function to output the
  430. // original value.
  431. err = out.Put(v.([2]any)[1])
  432. }
  433. if err != nil {
  434. return err
  435. }
  436. }
  437. return nil
  438. }
  439. func collectValues(fm *Frame, opts orderOptions, inputs Inputs) ([]any, error) {
  440. var values []any
  441. if opts.Key == nil {
  442. inputs(func(v any) { values = append(values, v) })
  443. } else {
  444. var keyErr error
  445. inputs(func(v any) {
  446. if keyErr != nil {
  447. return
  448. }
  449. outputs, err := fm.CaptureOutput(func(fm *Frame) error {
  450. return opts.Key.Call(fm, []any{v}, NoOpts)
  451. })
  452. if err != nil {
  453. keyErr = err
  454. } else if len(outputs) != 1 {
  455. keyErr = errors.New("&key function output more than one value")
  456. } else {
  457. t := [2]any{outputs[0], v}
  458. values = append(values, t)
  459. }
  460. })
  461. if keyErr != nil {
  462. return values, keyErr
  463. }
  464. }
  465. return values, nil
  466. }
  467. func getCmpFunction(fm *Frame, opts orderOptions, values []any, sortErr *error) func(i, j int) bool {
  468. if opts.Key != nil {
  469. // Use the default comparison but restricted to the key precomputed by
  470. // the `&key` function.
  471. return func(i, j int) bool {
  472. if *sortErr != nil {
  473. return true
  474. }
  475. ki := values[i].([2]any)[0]
  476. kj := values[j].([2]any)[0]
  477. o := cmp(ki, kj)
  478. if o == uncomparable {
  479. *sortErr = ErrUncomparable
  480. return true
  481. }
  482. if opts.Reverse {
  483. return o == more
  484. }
  485. return o == less
  486. }
  487. }
  488. if opts.LessThan != nil {
  489. // Use the custom function provided by the user to compare the value of
  490. // each item.
  491. return func(i, j int) bool {
  492. if *sortErr != nil {
  493. return true
  494. }
  495. var args []any
  496. if opts.Reverse {
  497. args = []any{values[j], values[i]}
  498. } else {
  499. args = []any{values[i], values[j]}
  500. }
  501. outputs, err := fm.CaptureOutput(func(fm *Frame) error {
  502. return opts.LessThan.Call(fm, args, NoOpts)
  503. })
  504. if err != nil {
  505. *sortErr = err
  506. return true
  507. }
  508. if len(outputs) != 1 {
  509. *sortErr = errs.BadValue{
  510. What: "output of the &less-than callback",
  511. Valid: "a single boolean",
  512. Actual: fmt.Sprintf("%d values", len(outputs))}
  513. return true
  514. }
  515. if b, ok := outputs[0].(bool); ok {
  516. return b
  517. }
  518. *sortErr = errs.BadValue{
  519. What: "output of the &less-than callback",
  520. Valid: "boolean", Actual: vals.Kind(outputs[0])}
  521. return true
  522. }
  523. }
  524. // Use the default comparison of each item. This is the common case when
  525. // there is no `&key` or `&less-than` option.
  526. return func(i, j int) bool {
  527. if *sortErr != nil {
  528. return true
  529. }
  530. o := cmp(values[i], values[j])
  531. if o == uncomparable {
  532. *sortErr = ErrUncomparable
  533. return true
  534. }
  535. if opts.Reverse {
  536. return o == more
  537. }
  538. return o == less
  539. }
  540. }