1. # A List is a (potentially infite) immutable list. The immutability is not
  2. # deep; a List may contain Scalar containers that can be assigned to. However,
  3. # it is not possible to shift/unshift/push/pop/splice/bind. A List is also
  4. # Positional, and so may be indexed.
  5. my class List does Iterable does Positional { # declared in BOOTSTRAP
  6. # class List is Cool
  7. # The reified elements in the list so far (that is, those that we already
  8. # have produced the values for).
  9. # has $!reified;
  10. #
  11. # Object that reifies the rest of the list. We don't just inline it into
  12. # the List class itself, because a STORE on Array can clear things and
  13. # upset an ongoing iteration. (An easy way to create such a case is to
  14. # assign an array with lazy parts into itself.)
  15. # has $!todo;
  16. # The object that goes into $!todo.
  17. class Reifier {
  18. # Our copy of the reified elements in the list so far.
  19. has $!reified;
  20. # The current iterator, if any, that we're working our way through in
  21. # order to lazily reify values. Must be depleted before $!future is
  22. # considered.
  23. has Iterator $!current-iter;
  24. # The (possibly lazy) values we've not yet incorporated into the list. The
  25. # only thing we can't simply copy from $!future into $!reified is a Slip
  26. # (and so the only reason to have a $!future is that there is at least one
  27. # Slip).
  28. has $!future;
  29. # The reification target (what .reify-* will .push to). Exists so we can
  30. # share the reification code between List/Array. List just uses its own
  31. # $!reified buffer; the Array one shoves stuff into Scalar containers
  32. # first.
  33. has $!reification-target;
  34. method reify-at-least(int $elems) {
  35. nqp::stmts(
  36. nqp::if(
  37. ($!current-iter.DEFINITE
  38. && nqp::eqaddr(
  39. $!current-iter.push-at-least(
  40. $!reification-target,
  41. nqp::sub_i($elems,nqp::elems($!reified))
  42. ),
  43. IterationEnd
  44. )),
  45. $!current-iter := Iterator
  46. ),
  47. # there is a future
  48. nqp::if(
  49. $!future.DEFINITE,
  50. # still need and can get something from the future
  51. nqp::stmts(
  52. nqp::while(
  53. (nqp::islt_i(nqp::elems($!reified),$elems)
  54. && nqp::elems($!future)),
  55. nqp::if(
  56. (nqp::istype((my $current := nqp::shift($!future)),Slip)
  57. && nqp::isconcrete($current)),
  58. nqp::stmts(
  59. (my $iter := $current.iterator),
  60. nqp::unless(
  61. nqp::eqaddr(
  62. $iter.push-at-least(
  63. $!reification-target,
  64. nqp::sub_i($elems,nqp::elems($!reified))
  65. ),
  66. IterationEnd
  67. ),
  68. # The iterator produced enough values to fill the need,
  69. # but did not reach its end. We save it for next time.
  70. # We know we'll exit the loop, since the < $elems check
  71. # must be False (unless the iterator broke contract).
  72. ($!current-iter := $iter)
  73. )
  74. ),
  75. $!reification-target.push($current)
  76. )
  77. ),
  78. # that was the future
  79. nqp::unless(
  80. nqp::elems($!future),
  81. ($!future := Mu)
  82. )
  83. )
  84. ),
  85. nqp::elems($!reified)
  86. )
  87. }
  88. method reify-until-lazy() {
  89. nqp::stmts(
  90. nqp::if(
  91. ($!current-iter.DEFINITE
  92. && nqp::eqaddr(
  93. $!current-iter.push-until-lazy($!reification-target),
  94. IterationEnd
  95. )
  96. ),
  97. $!current-iter := Iterator
  98. ),
  99. nqp::if(
  100. ($!future.DEFINITE && nqp::not_i($!current-iter.DEFINITE)),
  101. nqp::stmts(
  102. nqp::while(
  103. nqp::elems($!future),
  104. nqp::if(
  105. (nqp::istype((my $current := nqp::shift($!future)),Slip)
  106. && nqp::isconcrete($current)),
  107. nqp::unless(
  108. nqp::eqaddr(
  109. (my $iter := $current.iterator).push-until-lazy(
  110. $!reification-target),
  111. IterationEnd
  112. ),
  113. nqp::stmts(
  114. ($!current-iter := $iter),
  115. last
  116. )
  117. ),
  118. $!reification-target.push($current)
  119. )
  120. ),
  121. nqp::unless(
  122. nqp::elems($!future),
  123. $!future := Mu
  124. )
  125. )
  126. ),
  127. nqp::elems($!reified)
  128. )
  129. }
  130. method reify-all() {
  131. nqp::stmts(
  132. nqp::if(
  133. $!current-iter.DEFINITE,
  134. nqp::stmts(
  135. $!current-iter.push-all($!reification-target),
  136. $!current-iter := Iterator
  137. )
  138. ),
  139. nqp::if(
  140. $!future.DEFINITE,
  141. nqp::stmts(
  142. nqp::while(
  143. nqp::elems($!future),
  144. nqp::if(
  145. (nqp::istype((my $current := nqp::shift($!future)),Slip)
  146. && nqp::isconcrete($current)),
  147. $current.iterator.push-all($!reification-target),
  148. $!reification-target.push($current)
  149. )
  150. ),
  151. ($!future := Mu)
  152. )
  153. ),
  154. nqp::elems($!reified)
  155. )
  156. }
  157. method fully-reified() {
  158. !($!current-iter.DEFINITE || $!future.DEFINITE)
  159. }
  160. method is-lazy() {
  161. nqp::if(
  162. $!current-iter.DEFINITE,
  163. $!current-iter.is-lazy
  164. )
  165. }
  166. }
  167. method from-iterator(List:U: Iterator $iter) {
  168. nqp::stmts(
  169. (my \buffer := nqp::create(IterationBuffer)),
  170. nqp::bindattr(
  171. (my \result := nqp::create(self)),List,'$!reified',buffer),
  172. nqp::bindattr(
  173. (my \todo := nqp::create(Reifier)),Reifier,'$!reified',buffer),
  174. nqp::bindattr(todo,Reifier,'$!current-iter',$iter),
  175. # since Array has its own from-iterator, we don't need to
  176. # call reification-target, because it is the same as buffer
  177. nqp::bindattr(todo,Reifier,'$!reification-target',buffer),
  178. nqp::p6bindattrinvres(result,List,'$!todo',todo)
  179. )
  180. }
  181. method from-slurpy(|) {
  182. my \result := nqp::create(self);
  183. my Mu \vm-tuple := nqp::captureposarg(nqp::usecapture,1);
  184. nqp::if(
  185. nqp::isgt_i(nqp::elems(vm-tuple),0),
  186. nqp::stmts(
  187. nqp::bindattr(result,List,'$!reified',
  188. my \buffer := nqp::create(IterationBuffer)),
  189. nqp::bindattr(result,List,'$!todo',
  190. my \todo := nqp::create(List::Reifier)),
  191. nqp::bindattr(todo,List::Reifier,'$!reified',
  192. buffer),
  193. nqp::bindattr(todo,List::Reifier,'$!reification-target',
  194. result.reification-target),
  195. nqp::bindattr(todo,List::Reifier,'$!future',vm-tuple)
  196. )
  197. );
  198. result
  199. }
  200. method from-slurpy-onearg(|) {
  201. my Mu \vm-tuple := nqp::captureposarg(nqp::usecapture, 1);
  202. my $result;
  203. my $buffer;
  204. my $todo;
  205. my $consider;
  206. nqp::if(
  207. nqp::isgt_i(nqp::elems(vm-tuple),1),
  208. nqp::stmts( # handle as slurpy
  209. nqp::bindattr(($result := nqp::create(self)),List,'$!reified',
  210. $buffer := nqp::create(IterationBuffer)),
  211. nqp::bindattr($result,List,'$!todo',
  212. $todo := nqp::create(List::Reifier)),
  213. nqp::bindattr($todo,List::Reifier,'$!reified',
  214. $buffer),
  215. nqp::bindattr($todo,List::Reifier,'$!reification-target',
  216. $result.reification-target),
  217. nqp::bindattr($todo,List::Reifier,'$!future',vm-tuple),
  218. $result
  219. ),
  220. nqp::if(
  221. nqp::iseq_i(nqp::elems(vm-tuple),1),
  222. nqp::if( # single arg semantics active
  223. nqp::istype(($consider := nqp::atpos(vm-tuple,0)),Seq),
  224. nqp::if( # a single Seq
  225. nqp::istype(self,Array),
  226. $consider.cache,
  227. $consider
  228. ),
  229. nqp::stmts( # something else
  230. nqp::bindattr(($result := nqp::create(self)),List,'$!reified',
  231. $buffer := nqp::create(IterationBuffer)),
  232. nqp::bindattr($result,List,'$!todo',
  233. $todo := nqp::create(List::Reifier)),
  234. nqp::bindattr($todo,List::Reifier,'$!reified',
  235. $buffer),
  236. nqp::bindattr($todo,List::Reifier,'$!reification-target',
  237. $result.reification-target),
  238. nqp::if(
  239. nqp::iscont($consider)
  240. || nqp::not_i(nqp::istype($consider,Iterable))
  241. || nqp::not_i(nqp::p6definite($consider)),
  242. nqp::bindattr($todo,List::Reifier,'$!future',
  243. vm-tuple),
  244. nqp::bindattr($todo,List::Reifier,'$!future',
  245. nqp::list($consider.list.Slip))
  246. ),
  247. $result
  248. )
  249. ),
  250. nqp::create(self) # no args, so just a bare object
  251. )
  252. )
  253. }
  254. method from-slurpy-flat(|) {
  255. nqp::if(
  256. (my int $elems = nqp::elems(
  257. (my Mu $vm-tuple := nqp::captureposarg(nqp::usecapture,1))
  258. )),
  259. nqp::stmts(
  260. (my $future := nqp::setelems(nqp::create(IterationBuffer),$elems)),
  261. (my int $i = -1),
  262. (my int $b = 0),
  263. nqp::while(
  264. nqp::islt_i($i = nqp::add_i($i,1),$elems),
  265. nqp::if(
  266. nqp::iscont(my $consider := nqp::atpos($vm-tuple,$i)),
  267. nqp::bindpos($future,$i,$consider),
  268. nqp::if(
  269. (nqp::istype($consider,Iterable) && $consider.DEFINITE),
  270. nqp::if(
  271. nqp::istype($consider,PositionalBindFailover),
  272. nqp::bindpos($future,$i,$consider.cache.flat.Slip),
  273. nqp::bindpos($future,$i,$consider.flat.Slip)
  274. ),
  275. nqp::stmts(
  276. nqp::bindpos($future,$i,$consider),
  277. ($b = nqp::add_i($b,1))
  278. )
  279. )
  280. )
  281. ),
  282. nqp::if(
  283. nqp::iseq_i($b,$elems),
  284. # we already reified everything
  285. nqp::p6bindattrinvres(nqp::create(self),List,'$!reified',$future),
  286. # need full fledged List with a $todo
  287. nqp::stmts(
  288. (my $result :=
  289. nqp::p6bindattrinvres(nqp::create(self),List,'$!reified',
  290. (my $buffer := nqp::create(IterationBuffer))
  291. )
  292. ),
  293. (my $todo := nqp::create(List::Reifier)),
  294. nqp::bindattr($todo,List::Reifier,'$!reified',
  295. $buffer
  296. ),
  297. nqp::bindattr($todo,List::Reifier,'$!reification-target',
  298. $result.reification-target
  299. ),
  300. nqp::bindattr($todo,List::Reifier,'$!future',
  301. $future
  302. ),
  303. $todo.reify-until-lazy,
  304. nqp::unless(
  305. $todo.fully-reified,
  306. nqp::bindattr($result,List,'$!todo', $todo),
  307. ),
  308. $result
  309. )
  310. )
  311. ),
  312. # no args, an empty list suffices
  313. nqp::create(self)
  314. )
  315. }
  316. method new(**@things) {
  317. my \list = nqp::create(self);
  318. my \iterbuffer = nqp::create(IterationBuffer);
  319. nqp::bindattr(list, List, '$!reified', iterbuffer);
  320. my int $elems = +@things; # reify
  321. my int $i = -1;
  322. my $reified := nqp::getattr(@things,List,'$!reified');
  323. nqp::while( # doesn't sink
  324. nqp::islt_i($i = nqp::add_i($i,1),$elems),
  325. nqp::bindpos(iterbuffer,$i,(nqp::atpos($reified,$i)))
  326. );
  327. list
  328. }
  329. multi method Bool(List:D:) {
  330. nqp::p6bool(
  331. nqp::unless(
  332. ($!reified.DEFINITE && nqp::elems($!reified)),
  333. ($!todo.DEFINITE && $!todo.reify-at-least(1))
  334. )
  335. )
  336. }
  337. multi method Int(List:D:) { self.elems }
  338. multi method end(List:D:) { self.elems - 1 }
  339. multi method Numeric(List:D:) { self.elems }
  340. multi method Str(List:D:) { self.join(' ') }
  341. # Pretend we're a Match assuming we're a list of Matches
  342. method to() { self.elems ?? self[self.end].to !! Nil }
  343. method from() { self.elems ?? self[0].from !! Nil }
  344. method sum() is nodal {
  345. nqp::if(
  346. self.is-lazy,
  347. Failure.new(X::Cannot::Lazy.new(:action('.sum'))),
  348. nqp::if(
  349. $!reified.DEFINITE && (my int $elems = self.elems), # reifies
  350. nqp::stmts(
  351. (my $list := $!reified),
  352. (my $sum = nqp::ifnull(nqp::atpos($list,0),0)),
  353. (my int $i),
  354. nqp::while(
  355. nqp::islt_i($i = nqp::add_i($i,1),$elems),
  356. ($sum = $sum + nqp::ifnull(nqp::atpos($list,$i),0))
  357. ),
  358. $sum
  359. ),
  360. 0
  361. )
  362. )
  363. }
  364. proto method fmt(|) { * }
  365. multi method fmt() {
  366. nqp::if(
  367. (my int $elems = self.elems), # reifies
  368. nqp::stmts(
  369. (my $list := $!reified),
  370. (my $strings := nqp::setelems(nqp::list_s,$elems)),
  371. (my int $i = -1),
  372. nqp::while(
  373. nqp::islt_i(($i = nqp::add_i($i,1)),$elems),
  374. nqp::bindpos_s($strings,$i,nqp::atpos($list,$i).Str)
  375. ),
  376. nqp::p6box_s(nqp::join(' ',$strings))
  377. ),
  378. ''
  379. )
  380. }
  381. multi method fmt(Str(Cool) $format) {
  382. nqp::if(
  383. nqp::iseq_s($format,'%s'),
  384. self.fmt,
  385. self.fmt($format,' ')
  386. )
  387. }
  388. multi method fmt(Str(Cool) $format, $separator) {
  389. nqp::if(
  390. nqp::iseq_s($format,'%s') && nqp::iseq_s($separator,' '),
  391. self.fmt,
  392. nqp::if(
  393. (my int $elems = self.elems), # reifies
  394. nqp::stmts(
  395. (my $list := $!reified),
  396. (my $strings := nqp::setelems(nqp::list_s,$elems)),
  397. (my int $i = -1),
  398. nqp::if(
  399. nqp::iseq_i( # only one % in format?
  400. nqp::elems(nqp::split('%',$format)),
  401. 2
  402. ) && nqp::iseq_i( # only one %s in format
  403. nqp::elems(my $parts := nqp::split('%s',$format)),
  404. 2
  405. ),
  406. nqp::while( # only a single %s
  407. nqp::islt_i(($i = nqp::add_i($i,1)),$elems),
  408. nqp::bindpos_s($strings,$i,
  409. nqp::join(nqp::atpos($list,$i).Str,$parts)
  410. )
  411. ),
  412. nqp::while( # something else
  413. nqp::islt_i(($i = nqp::add_i($i,1)),$elems),
  414. nqp::bindpos_s($strings,$i,
  415. nqp::atpos($list,$i).fmt($format)
  416. )
  417. )
  418. ),
  419. nqp::p6box_s(nqp::join($separator,$strings))
  420. ),
  421. ''
  422. )
  423. )
  424. }
  425. multi method elems(List:D:) is nodal {
  426. nqp::if(
  427. $!todo.DEFINITE,
  428. nqp::stmts(
  429. $!todo.reify-until-lazy,
  430. nqp::if(
  431. $!todo.fully-reified,
  432. nqp::stmts(
  433. ($!todo := nqp::null),
  434. nqp::elems($!reified)
  435. ),
  436. Failure.new(X::Cannot::Lazy.new(:action('.elems')))
  437. )
  438. ),
  439. nqp::if(
  440. $!reified.DEFINITE,
  441. nqp::elems($!reified),
  442. 0
  443. )
  444. )
  445. }
  446. multi method AT-POS(List:D: Int:D $pos) is raw {
  447. nqp::if(
  448. nqp::islt_i($pos,0),
  449. Failure.new(X::OutOfRange.new(
  450. :what($*INDEX // 'Index'), :got($pos), :range<0..^Inf>)),
  451. nqp::if(
  452. $!reified.DEFINITE,
  453. nqp::ifnull(
  454. nqp::atpos($!reified,$pos),
  455. nqp::if(
  456. ($!todo.DEFINITE && $!todo.reify-at-least(nqp::add_i($pos,1))),
  457. nqp::ifnull(nqp::atpos($!reified,$pos),Nil),
  458. Nil
  459. )
  460. ),
  461. Nil
  462. )
  463. )
  464. }
  465. multi method AT-POS(List:D: int $pos) is raw {
  466. nqp::if(
  467. nqp::islt_i($pos,0),
  468. Failure.new(X::OutOfRange.new(
  469. :what($*INDEX // 'Index'), :got($pos), :range<0..^Inf>)),
  470. nqp::if(
  471. $!reified.DEFINITE,
  472. nqp::if(
  473. nqp::islt_i($pos,nqp::elems($!reified)),
  474. nqp::atpos($!reified,$pos),
  475. nqp::if(
  476. ($!todo.DEFINITE && $!todo.reify-at-least(nqp::add_i($pos,1))),
  477. nqp::ifnull(nqp::atpos($!reified,$pos),Nil),
  478. Nil
  479. )
  480. ),
  481. Nil
  482. )
  483. )
  484. }
  485. method BIND-POS(List:D: Int:D \pos, \what) is raw {
  486. nqp::iscont(self.AT-POS(pos))
  487. ?? nqp::bindpos($!reified,nqp::unbox_i(pos),what)
  488. !! X::Bind.new.throw
  489. }
  490. multi method EXISTS-POS(List:D: int $pos) {
  491. nqp::p6bool(
  492. nqp::if(
  493. nqp::isge_i($pos,0),
  494. nqp::if(
  495. $!reified.DEFINITE && nqp::islt_i($pos,nqp::elems($!reified)),
  496. nqp::existspos($!reified,$pos),
  497. nqp::if(
  498. $!todo.DEFINITE,
  499. nqp::stmts(
  500. $!todo.reify-at-least(nqp::add_i($pos,1)),
  501. nqp::existspos($!reified,$pos)
  502. )
  503. )
  504. )
  505. )
  506. )
  507. }
  508. multi method EXISTS-POS(List:D: Int:D $pos) {
  509. nqp::p6bool(
  510. nqp::if(
  511. nqp::isge_i($pos,0),
  512. nqp::if(
  513. $!reified.DEFINITE && nqp::islt_i($pos,nqp::elems($!reified)),
  514. nqp::existspos($!reified,$pos),
  515. nqp::if(
  516. $!todo.DEFINITE,
  517. nqp::stmts(
  518. $!todo.reify-at-least(nqp::add_i($pos,1)),
  519. nqp::existspos($!reified,$pos)
  520. )
  521. )
  522. )
  523. )
  524. )
  525. }
  526. method reification-target(List:D:) {
  527. nqp::ifnull(
  528. $!reified,
  529. $!reified := nqp::create(IterationBuffer)
  530. )
  531. }
  532. method iterator(List:D:) {
  533. # something to iterate over in the future
  534. nqp::if(
  535. $!todo.DEFINITE,
  536. class :: does Iterator {
  537. has int $!i;
  538. has $!list;
  539. has $!reified;
  540. has $!todo;
  541. method !SET-SELF(\list) {
  542. $!i = -1;
  543. $!list := list;
  544. $!reified := nqp::getattr(list,List,'$!reified').DEFINITE
  545. # we already have a place to put values in
  546. ?? nqp::getattr(list,List,'$!reified')
  547. # create a place here and there to put values in
  548. !! nqp::bindattr(list,List,'$!reified',
  549. nqp::create(IterationBuffer));
  550. $!todo := nqp::getattr(list, List, '$!todo');
  551. self
  552. }
  553. method new(\list) { nqp::create(self)!SET-SELF(list) }
  554. method pull-one() is raw {
  555. nqp::ifnull(
  556. nqp::atpos($!reified,$!i = nqp::add_i($!i,1)),
  557. $!todo.DEFINITE
  558. ?? nqp::islt_i($!i,$!todo.reify-at-least(nqp::add_i($!i,1)))
  559. ?? nqp::atpos($!reified,$!i)
  560. !! self!done
  561. !! IterationEnd
  562. )
  563. }
  564. method !done() is raw {
  565. $!todo := nqp::bindattr($!list,List,'$!todo',nqp::null);
  566. IterationEnd
  567. }
  568. method push-until-lazy($target) {
  569. if $!todo.DEFINITE {
  570. my int $elems = $!todo.reify-until-lazy;
  571. nqp::while( # doesn't sink
  572. nqp::islt_i($!i = nqp::add_i($!i,1),$elems),
  573. $target.push(nqp::atpos($!reified,$!i))
  574. );
  575. nqp::if(
  576. $!todo.fully-reified,
  577. self!done,
  578. nqp::stmts(
  579. ($!i = $elems - 1),
  580. Mu
  581. )
  582. )
  583. }
  584. else {
  585. my int $elems = nqp::elems($!reified);
  586. nqp::while( # doesn't sink
  587. nqp::islt_i($!i = nqp::add_i($!i,1),$elems),
  588. $target.push(nqp::atpos($!reified,$!i))
  589. );
  590. IterationEnd
  591. }
  592. }
  593. method is-lazy() { $!todo.DEFINITE && $!todo.is-lazy }
  594. }.new(self),
  595. # everything we need is already there
  596. nqp::if(
  597. $!reified.DEFINITE,
  598. Rakudo::Iterator.ReifiedList(self),
  599. Rakudo::Iterator.Empty
  600. )
  601. )
  602. }
  603. multi method ACCEPTS(List:D: $topic) {
  604. unless nqp::istype($topic, Iterable) {
  605. return self unless self.elems;
  606. return self if nqp::istype(self[0], Match);
  607. return False;
  608. }
  609. my $sseq = self;
  610. my $tseq = $topic;
  611. sub tailmatch($s,$t) {
  612. my int $spos = $s;
  613. my int $tpos = $t;
  614. while $spos < $sseq {
  615. # if the next element is Whatever
  616. if nqp::istype($sseq[$spos], HyperWhatever) {
  617. # skip over all of the Whatevers
  618. $spos = $spos + 1
  619. while $spos <= $sseq && nqp::istype($sseq[$spos], HyperWhatever);
  620. # if nothing left, we're done
  621. return True if $spos == $sseq;
  622. # find a target matching our new target
  623. while $tpos < $tseq {
  624. my $result = tailmatch($spos,$tpos);
  625. return True if $result;
  626. $tpos = $tpos + 1
  627. }
  628. # return false if we ran out
  629. return False;
  630. }
  631. elsif $tpos == $tseq or not $sseq[$spos].ACCEPTS($tseq[$tpos] ) {
  632. return False;
  633. }
  634. # skip matching elements
  635. $spos = $spos + 1;
  636. $tpos = $tpos + 1;
  637. }
  638. # If nothing left to match, we're successful.
  639. $tpos >= $tseq;
  640. }
  641. tailmatch(0,0);
  642. }
  643. multi method list(List:D:) { self }
  644. proto method Seq(|) is nodal { * }
  645. multi method Seq(List:D:) { Seq.new(self.iterator) }
  646. method sink(--> Nil) { }
  647. multi method values(List:D:) {
  648. Seq.new(self.iterator)
  649. }
  650. multi method keys(List:D:) {
  651. Seq.new(nqp::if(
  652. self.is-lazy,
  653. nqp::stmts(
  654. (my int $i = -1),
  655. Rakudo::Iterator.Callable( { $i = nqp::add_i($i,1) }, True )
  656. ),
  657. Rakudo::Iterator.IntRange(0, self.elems - 1)
  658. ))
  659. }
  660. multi method kv(List:D:) {
  661. Seq.new(Rakudo::Iterator.KeyValue(self.iterator))
  662. }
  663. multi method pairs(List:D:) {
  664. Seq.new(Rakudo::Iterator.Pair(self.iterator))
  665. }
  666. multi method antipairs(List:D:) {
  667. Seq.new(Rakudo::Iterator.AntiPair(self.iterator))
  668. }
  669. multi method invert(List:D:) {
  670. Seq.new(Rakudo::Iterator.Invert(self.iterator))
  671. }
  672. # Store in List targets containers with in the list. This handles list
  673. # assignments, like ($a, $b) = foo().
  674. proto method STORE(|) { * }
  675. multi method STORE(List:D: Iterable:D \iterable) {
  676. # First pass -- scan lhs containers and pick out scalar versus list
  677. # assignment. This also reifies the RHS values we need, and deconts
  678. # them. The decont is needed so that we can do ($a, $b) = ($b, $a).
  679. my \cv = nqp::list();
  680. my \lhs-iter = self.iterator;
  681. my \rhs-iter = iterable.iterator;
  682. my int $rhs-done;
  683. my Mu $v;
  684. my Mu $c;
  685. my Mu $sub-iter;
  686. my Mu $sc;
  687. nqp::until(
  688. nqp::eqaddr(($c := lhs-iter.pull-one),IterationEnd),
  689. nqp::if( # Container: scalar assignment
  690. nqp::iscont($c),
  691. nqp::stmts(
  692. nqp::push(cv,$c),
  693. nqp::if(
  694. ($rhs-done || ($rhs-done =
  695. nqp::eqaddr(($v := rhs-iter.pull-one),IterationEnd))),
  696. nqp::push(cv,Nil),
  697. nqp::push(cv,nqp::decont($v)),
  698. )
  699. ),
  700. nqp::if( # Whatever: skip assigning value
  701. nqp::istype($c,Whatever),
  702. nqp::if(
  703. (nqp::not_i($rhs-done)
  704. && nqp::eqaddr(rhs-iter.pull-one,IterationEnd)),
  705. ($rhs-done = 1)
  706. ),
  707. nqp::if( # List splice into current lhs
  708. (nqp::istype($c,List) && nqp::not_i(nqp::istype($c,Array))),
  709. nqp::stmts(
  710. ($sub-iter := $c.iterator),
  711. nqp::until(
  712. nqp::eqaddr(($sc := $sub-iter.pull-one),IterationEnd),
  713. nqp::stmts(
  714. nqp::push(cv,$sc);
  715. nqp::if(
  716. ($rhs-done = nqp::eqaddr(
  717. ($v := rhs-iter.pull-one),IterationEnd
  718. )),
  719. nqp::push(cv,Nil),
  720. nqp::push(cv,nqp::decont($v))
  721. )
  722. )
  723. )
  724. ),
  725. nqp::stmts( # Non-container: store entire remaining rhs
  726. nqp::push(cv,$c),
  727. nqp::push(cv,List.from-iterator(rhs-iter)),
  728. ($rhs-done = 1)
  729. )
  730. )
  731. )
  732. )
  733. );
  734. # Second pass, perform the assignments.
  735. nqp::shift(cv) = nqp::shift(cv) while nqp::elems(cv);
  736. self
  737. }
  738. multi method STORE(List:D: Mu \item) {
  739. self.STORE((item,));
  740. }
  741. multi method gist(List:D:) {
  742. self.gistseen('List', {
  743. '(' ~ self.map( -> $elem {
  744. given ++$ {
  745. when 101 { '...' }
  746. when 102 { last }
  747. default { $elem.gist }
  748. }
  749. }).join(' ') ~ ')'
  750. })
  751. }
  752. multi method perl(List:D \SELF:) {
  753. SELF.perlseen('List', {
  754. '$' x nqp::iscont(SELF) ~ '('
  755. ~ (self.elems == 1 ?? self[0].perl ~ ',' !! self.map({.perl}).join(', '))
  756. ~ ' ' x nqp::istrue(self.not && nqp::iscont(SELF)) # add space to avoid `$()`
  757. ~ ')'
  758. })
  759. }
  760. multi method List(List:D:) { self }
  761. multi method Slip(List:D:) {
  762. nqp::if(
  763. $!todo.DEFINITE,
  764. # We're not fully reified, and so have internal mutability still.
  765. # The safe thing to do is to take an iterator of ourself and build
  766. # the Slip out of that.
  767. Slip.from-iterator(self.iterator),
  768. # We're fully reified - and so immutable inside and out! Just make
  769. # a Slip that shares our reified buffer.
  770. nqp::p6bindattrinvres(nqp::create(Slip),List,'$!reified',$!reified)
  771. )
  772. }
  773. multi method Array(List:D:) {
  774. # We need to populate the Array slots with Scalar containers
  775. nqp::if(
  776. $!todo.DEFINITE,
  777. Array.from-iterator(self.iterator),
  778. nqp::if(
  779. $!reified.DEFINITE,
  780. nqp::stmts(
  781. (my int $elems = nqp::elems($!reified)),
  782. (my $array := nqp::setelems(nqp::create(IterationBuffer),$elems)),
  783. (my int $i = -1),
  784. nqp::while(
  785. nqp::islt_i(($i = nqp::add_i($i,1)),$elems),
  786. nqp::bindpos($array, $i,
  787. nqp::assign(
  788. nqp::p6scalarfromdesc(nqp::null),
  789. nqp::atpos($!reified,$i)
  790. )
  791. )
  792. ),
  793. nqp::p6bindattrinvres(nqp::create(Array),List,'$!reified',$array)
  794. ),
  795. nqp::create(Array)
  796. )
  797. )
  798. }
  799. method eager {
  800. nqp::stmts(
  801. nqp::if(
  802. $!todo.DEFINITE,
  803. nqp::stmts(
  804. $!todo.reify-all,
  805. ($!todo := nqp::null)
  806. )
  807. ),
  808. self
  809. )
  810. }
  811. method Capture() {
  812. fail X::Cannot::Lazy.new(:action('create a Capture from'))
  813. if self.is-lazy;
  814. # we have something to work with
  815. if $!reified.DEFINITE && nqp::elems($!reified) -> int $elems {
  816. my $capture := nqp::create(Capture);
  817. my $list := nqp::create(IterationBuffer);
  818. my $hash := nqp::hash;
  819. my int $i = -1;
  820. my $v;
  821. nqp::istype(($v := nqp::atpos($!reified, $i)),Pair)
  822. ?? nqp::bindkey($hash, $v.key.Str, $v.value)
  823. !! nqp::push($list,$v)
  824. while nqp::islt_i($i = nqp::add_i($i,1),$elems);
  825. nqp::bindattr($capture,Capture,'@!list',$list) if nqp::elems($list);
  826. nqp::bindattr($capture,Capture,'%!hash',$hash) if nqp::elems($hash);
  827. $capture
  828. }
  829. # nothing to work with
  830. else {
  831. nqp::create(Capture)
  832. }
  833. }
  834. method FLATTENABLE_LIST() {
  835. nqp::if(
  836. $!todo.DEFINITE,
  837. nqp::stmts(
  838. $!todo.reify-all,
  839. $!reified
  840. ),
  841. nqp::if(
  842. $!reified.DEFINITE,
  843. $!reified,
  844. nqp::bindattr(self,List,'$!reified',nqp::create(IterationBuffer))
  845. )
  846. )
  847. }
  848. method FLATTENABLE_HASH() { nqp::hash() }
  849. method Supply(List:D:) { Supply.from-list(self) }
  850. method CALL-ME(List:U: |c) {
  851. self.new(|c);
  852. }
  853. multi method is-lazy(List:D:) {
  854. nqp::if(
  855. $!todo.DEFINITE,
  856. nqp::stmts(
  857. $!todo.reify-until-lazy,
  858. nqp::if(
  859. $!todo.fully-reified,
  860. nqp::p6bool($!todo := nqp::null),
  861. True
  862. )
  863. )
  864. )
  865. }
  866. proto method pick(|) is nodal { * }
  867. multi method pick(List:D:) {
  868. self.is-lazy
  869. ?? Failure.new(X::Cannot::Lazy.new(:action('.pick from')))
  870. !! (my Int $elems = self.elems)
  871. ?? nqp::atpos($!reified, $elems.rand.floor)
  872. !! Nil
  873. }
  874. multi method pick(List:D: Callable:D $calculate) {
  875. self.is-lazy
  876. ?? Failure.new(X::Cannot::Lazy.new(:action('.pick from')))
  877. !! self.pick( $calculate(self.elems) )
  878. }
  879. multi method pick(List:D: $number is copy) {
  880. fail X::Cannot::Lazy.new(:action('.pick from')) if self.is-lazy;
  881. my Int $elems = self.elems;
  882. return () unless $elems;
  883. $number = nqp::istype($number,Whatever) || $number == Inf
  884. ?? $elems
  885. !! $number.UInt min $elems;
  886. Seq.new(class :: does Iterator {
  887. has $!list;
  888. has Int $!elems;
  889. has int $!number;
  890. method !SET-SELF(\list,$!elems,\number) {
  891. $!list := nqp::clone(nqp::getattr(list,List,'$!reified'));
  892. $!number = number + 1;
  893. self
  894. }
  895. method new(\list,\elems,\number) {
  896. nqp::create(self)!SET-SELF(list,elems,number)
  897. }
  898. method pull-one() {
  899. if ($!number = nqp::sub_i($!number,1)) {
  900. my int $i;
  901. my \tmp = nqp::atpos($!list,$i = $!elems.rand.floor);
  902. nqp::bindpos($!list,$i,
  903. nqp::atpos($!list,nqp::unbox_i(--$!elems))
  904. );
  905. tmp
  906. }
  907. else {
  908. IterationEnd
  909. }
  910. }
  911. method push-all($target --> IterationEnd) {
  912. my int $i;
  913. nqp::while(
  914. ($!number = nqp::sub_i($!number,1)),
  915. nqp::stmts( # doesn't sink
  916. ($target.push(nqp::atpos($!list,$i = $!elems.rand.floor))),
  917. (nqp::bindpos($!list,$i,
  918. nqp::atpos($!list,nqp::unbox_i(--$!elems))))
  919. )
  920. )
  921. }
  922. }.new(self,$elems,$number))
  923. }
  924. proto method roll(|) is nodal { * }
  925. multi method roll() {
  926. self.is-lazy
  927. ?? Failure.new(X::Cannot::Lazy.new(:action('.roll from')))
  928. !! (my Int $elems = self.elems)
  929. ?? nqp::atpos($!reified, $elems.rand.floor)
  930. !! Nil
  931. }
  932. multi method roll(Whatever) {
  933. nqp::if(
  934. self.is-lazy,
  935. X::Cannot::Lazy.new(:action('.roll from')).throw,
  936. Seq.new(nqp::if(
  937. self.elems,
  938. Rakudo::Iterator.Roller(self),
  939. Rakudo::Iterator.Empty
  940. ))
  941. )
  942. }
  943. multi method roll(\number) {
  944. number == Inf
  945. ?? self.roll(*)
  946. !! self.is-lazy
  947. ?? X::Cannot::Lazy.new(:action('.roll from')).throw
  948. !! self.elems # this allocates/reifies
  949. ?? Seq.new(class :: does Iterator {
  950. has $!list;
  951. has Int $!elems;
  952. has int $!todo;
  953. method !SET-SELF(\list,\todo) {
  954. $!list := nqp::getattr(list,List,'$!reified');
  955. $!elems = nqp::elems($!list);
  956. $!todo = todo;
  957. self
  958. }
  959. method new(\list,\todo) {
  960. nqp::create(self)!SET-SELF(list,todo)
  961. }
  962. method pull-one() is raw {
  963. if $!todo {
  964. $!todo = $!todo - 1;
  965. nqp::atpos($!list,$!elems.rand.floor)
  966. }
  967. else {
  968. IterationEnd
  969. }
  970. }
  971. }.new(self,number.Int))
  972. !! Seq.new(Rakudo::Iterator.Empty)
  973. }
  974. method reverse() is nodal {
  975. nqp::if(
  976. self.is-lazy, # reifies
  977. Failure.new(X::Cannot::Lazy.new(:action<reverse>)),
  978. Seq.new(nqp::if(
  979. $!reified,
  980. Rakudo::Iterator.ReifiedListReverse($!reified),
  981. Rakudo::Iterator.Empty
  982. ))
  983. )
  984. }
  985. method rotate(Int(Cool) $rotate = 1) is nodal {
  986. nqp::if(
  987. self.is-lazy, # reifies
  988. Failure.new(X::Cannot::Lazy.new(:action<rotate>)),
  989. nqp::if(
  990. $!reified,
  991. Rakudo::Internals.RotateListToList(
  992. self, $rotate,
  993. nqp::p6bindattrinvres(nqp::create(self),List,'$!reified',
  994. nqp::setelems(
  995. nqp::create(IterationBuffer),nqp::elems($!reified)
  996. )
  997. )
  998. ),
  999. nqp::create(self)
  1000. )
  1001. )
  1002. }
  1003. proto method combinations(|) is nodal {*}
  1004. multi method combinations() {
  1005. nqp::stmts(
  1006. (my int $elems = self.elems), # reifies
  1007. (my int $i = -1),
  1008. Seq.new(
  1009. Rakudo::Iterator.SequentialIterators(
  1010. Rakudo::Iterator.Callable( {
  1011. nqp::if(
  1012. nqp::islt_i(($i = nqp::add_i($i,1)),$elems),
  1013. Rakudo::Iterator.ListIndexes( # basically .combinations($i)
  1014. self,
  1015. Rakudo::Iterator.Combinations($elems, $i, 1)
  1016. ),
  1017. nqp::if(
  1018. nqp::iseq_i($i,$elems),
  1019. Rakudo::Iterator.OneValue( # last one is self
  1020. nqp::p6bindattrinvres( # but must be a (new) List
  1021. nqp::create(List), # so transplant innards
  1022. List,
  1023. '$!reified',
  1024. nqp::getattr(self,List,'$!reified')
  1025. )
  1026. ),
  1027. IterationEnd
  1028. )
  1029. )
  1030. } )
  1031. )
  1032. )
  1033. )
  1034. }
  1035. multi method combinations(Int() $of) {
  1036. Seq.new(
  1037. Rakudo::Iterator.ListIndexes(
  1038. self, Rakudo::Iterator.Combinations( self.elems, $of, 1)
  1039. )
  1040. )
  1041. }
  1042. multi method combinations(Range:D $ofrange) {
  1043. nqp::stmts(
  1044. (my int $elems = self.elems), # reifies
  1045. $ofrange.int-bounds(my int $i, my int $to),
  1046. ($i = nqp::if(nqp::islt_i($i,0),-1,nqp::sub_i($i,1))),
  1047. nqp::if(nqp::isgt_i($to,$elems),($to = $elems)),
  1048. Seq.new(
  1049. Rakudo::Iterator.SequentialIterators(
  1050. Rakudo::Iterator.Callable( {
  1051. nqp::if(
  1052. nqp::isle_i(($i = nqp::add_i($i,1)),$to),
  1053. Rakudo::Iterator.ListIndexes( # basically .combinations($i)
  1054. self,
  1055. Rakudo::Iterator.Combinations($elems, $i, 1)
  1056. ),
  1057. IterationEnd
  1058. )
  1059. } )
  1060. )
  1061. )
  1062. )
  1063. }
  1064. proto method permutations(|) is nodal {*}
  1065. multi method permutations() {
  1066. Seq.new(
  1067. Rakudo::Iterator.ListIndexes(
  1068. self, Rakudo::Iterator.Permutations( self.elems, 1)
  1069. )
  1070. )
  1071. }
  1072. method join(List:D: Str(Cool) $separator = '') is nodal {
  1073. nqp::stmts(
  1074. nqp::if(
  1075. $!todo.DEFINITE,
  1076. nqp::stmts(
  1077. $!todo.reify-until-lazy,
  1078. nqp::if(
  1079. $!todo.fully-reified,
  1080. ($!todo := nqp::null),
  1081. (my int $infinite = 1)
  1082. )
  1083. )
  1084. ),
  1085. nqp::if(
  1086. $!reified.DEFINITE
  1087. && (my int $elems = nqp::elems($!reified)),
  1088. nqp::stmts( # something to join
  1089. (my $strings :=
  1090. nqp::setelems(nqp::list_s,nqp::add_i($elems,$infinite))),
  1091. (my int $i = -1),
  1092. nqp::while(
  1093. nqp::islt_i(($i = nqp::add_i($i,1)),$elems),
  1094. nqp::bindpos_s($strings,$i,nqp::if(
  1095. nqp::isnull(my $tmp := nqp::atpos($!reified,$i)),
  1096. '',
  1097. nqp::if(
  1098. nqp::isconcrete($tmp) && nqp::istype($tmp,Str),
  1099. $tmp,
  1100. nqp::if(
  1101. nqp::can($tmp,'Str'),
  1102. $tmp.Str,
  1103. nqp::box_s($tmp,Str)
  1104. )
  1105. )
  1106. ))
  1107. ),
  1108. nqp::if($infinite,nqp::bindpos_s($strings,$i,'...')),
  1109. nqp::p6box_s(nqp::join($separator,$strings))
  1110. ),
  1111. nqp::if($infinite,'...','')
  1112. )
  1113. )
  1114. }
  1115. # https://en.wikipedia.org/wiki/Merge_sort#Bottom-up_implementation
  1116. multi method sort(List:D:) {
  1117. nqp::stmts(
  1118. nqp::if(
  1119. $!todo.DEFINITE,
  1120. nqp::stmts(
  1121. $!todo.reify-until-lazy,
  1122. nqp::if(
  1123. $!todo.fully-reified,
  1124. ($!todo := nqp::null),
  1125. X::Cannot::Lazy.new(:action('.sort')).throw
  1126. )
  1127. )
  1128. ),
  1129. Seq.new(
  1130. nqp::if(
  1131. $!reified.DEFINITE,
  1132. Rakudo::Iterator.ReifiedList(
  1133. Rakudo::Sorting.MERGESORT-REIFIED-LIST(
  1134. nqp::p6bindattrinvres(
  1135. nqp::create(List),List,'$!reified',
  1136. nqp::clone(nqp::getattr(self,List,'$!reified'))
  1137. )
  1138. )
  1139. ),
  1140. Rakudo::Iterator.Empty
  1141. )
  1142. )
  1143. )
  1144. }
  1145. multi method sort(List:D: &by) {
  1146. nqp::stmts(
  1147. nqp::if(
  1148. $!todo.DEFINITE,
  1149. nqp::stmts(
  1150. $!todo.reify-until-lazy,
  1151. nqp::if(
  1152. $!todo.fully-reified,
  1153. ($!todo := nqp::null),
  1154. X::Cannot::Lazy.new(:action('.sort')).throw
  1155. )
  1156. )
  1157. ),
  1158. Seq.new(
  1159. nqp::if(
  1160. $!reified.DEFINITE,
  1161. Rakudo::Iterator.ReifiedList(
  1162. nqp::if(
  1163. nqp::eqaddr(&by,&infix:<cmp>),
  1164. Rakudo::Sorting.MERGESORT-REIFIED-LIST(
  1165. nqp::p6bindattrinvres(nqp::create(List),List,'$!reified',
  1166. nqp::clone(nqp::getattr(self,List,'$!reified')))
  1167. ),
  1168. nqp::if(
  1169. &by.count < 2,
  1170. Rakudo::Sorting.MERGESORT-REIFIED-LIST-AS(
  1171. nqp::p6bindattrinvres(nqp::create(List),List,'$!reified',
  1172. nqp::getattr(self,List,'$!reified')),
  1173. &by
  1174. ),
  1175. Rakudo::Sorting.MERGESORT-REIFIED-LIST-WITH(
  1176. nqp::p6bindattrinvres(nqp::create(List),List,'$!reified',
  1177. nqp::clone(nqp::getattr(self,List,'$!reified'))),
  1178. &by
  1179. )
  1180. )
  1181. )
  1182. ),
  1183. Rakudo::Iterator.Empty
  1184. )
  1185. )
  1186. )
  1187. }
  1188. method collate {
  1189. self.sort(&[coll]);
  1190. }
  1191. multi method tail(List:D:) is raw {
  1192. nqp::if(
  1193. $!todo.DEFINITE,
  1194. self.Any::tail,
  1195. nqp::if(
  1196. $!reified.DEFINITE && nqp::elems($!reified),
  1197. nqp::atpos($!reified,nqp::sub_i(nqp::elems($!reified),1)),
  1198. Nil
  1199. )
  1200. )
  1201. }
  1202. multi method tail(List:D: $n) {
  1203. nqp::if(
  1204. $!todo.DEFINITE,
  1205. self.Any::tail($n),
  1206. Seq.new(
  1207. nqp::if(
  1208. $!reified.DEFINITE && nqp::elems($!reified),
  1209. nqp::stmts(
  1210. (my $iterator := Rakudo::Iterator.ReifiedList(self)),
  1211. nqp::if(
  1212. nqp::istype($n,Callable)
  1213. && nqp::isgt_i((my $skip := -($n(0).Int)),0),
  1214. $iterator.skip-at-least($skip),
  1215. nqp::unless(
  1216. nqp::istype($n,Whatever) || $n == Inf,
  1217. $iterator.skip-at-least(nqp::elems($!reified) - $n)
  1218. )
  1219. ),
  1220. $iterator
  1221. ),
  1222. Rakudo::Iterator.Empty
  1223. )
  1224. )
  1225. )
  1226. }
  1227. method push(|) is nodal {
  1228. X::Immutable.new(:typename<List>,:method<push>).throw
  1229. }
  1230. method append(|) is nodal {
  1231. X::Immutable.new(:typename<List>,:method<append>).throw
  1232. }
  1233. method unshift(|) is nodal {
  1234. X::Immutable.new(:typename<List>,:method<unshift>).throw
  1235. }
  1236. method prepend(|) is nodal {
  1237. X::Immutable.new(:typename<List>,:method<prepend>).throw
  1238. }
  1239. method shift(|) is nodal {
  1240. X::Immutable.new(:typename<List>,:method<shift>).throw
  1241. }
  1242. method pop(|) is nodal {
  1243. X::Immutable.new(:typename<List>, :method<pop>).throw
  1244. }
  1245. }
  1246. # The , operator produces a List.
  1247. proto sub infix:<,>(|) is pure {*}
  1248. multi sub infix:<,>() { nqp::create(List) }
  1249. multi sub infix:<,>(|) {
  1250. # look for a Slip in the parameters
  1251. my \in := nqp::p6argvmarray();
  1252. my int $i = -1;
  1253. my int $elems = nqp::elems(in);
  1254. nqp::while(
  1255. (nqp::islt_i(($i = nqp::add_i($i,1)),$elems)
  1256. && nqp::not_i(nqp::istype(nqp::atpos(in,$i),Slip))),
  1257. nqp::null
  1258. );
  1259. nqp::if(
  1260. nqp::iseq_i($i,$elems), # no Slip seen, so just alias input params
  1261. nqp::p6bindattrinvres(nqp::create(List),List,'$!reified',in),
  1262. nqp::stmts( # Slip seen, first copy non-slippy things
  1263. ($elems = $i),
  1264. ($i = -1),
  1265. (my $reified := nqp::setelems(nqp::create(IterationBuffer),$elems)),
  1266. nqp::while(
  1267. nqp::islt_i(($i = nqp::add_i($i,1)),$elems),
  1268. nqp::bindpos($reified,$i,nqp::shift(in))
  1269. ),
  1270. # now set up the List with a future
  1271. (my $list :=
  1272. nqp::p6bindattrinvres(nqp::create(List),List,'$!reified',$reified)),
  1273. nqp::bindattr($list,List,'$!todo',
  1274. my $todo:= nqp::create(List::Reifier)),
  1275. nqp::bindattr($todo,List::Reifier,'$!reified',$reified),
  1276. nqp::bindattr($todo,List::Reifier,'$!future',in),
  1277. nqp::bindattr($todo,List::Reifier,'$!reification-target',$reified),
  1278. $list
  1279. )
  1280. )
  1281. }
  1282. sub combinations(Int() $n, Int() $k) {
  1283. Seq.new(Rakudo::Iterator.Combinations($n,$k,0))
  1284. }
  1285. sub permutations(Int() $n) {
  1286. Seq.new(Rakudo::Iterator.Permutations($n,0))
  1287. }
  1288. sub list(+l) { l }
  1289. # Use **@list and then .flat it, otherwise we'll end up remembering all the
  1290. # things we flatten, which would be different semantics to .flat which gives
  1291. # back a Seq. We also add an Iterable candidate, to preserve .is-lazy
  1292. # of an Iterable whenever we can.
  1293. proto flat(|) {*}
  1294. multi flat(**@list is raw) { @list.flat }
  1295. multi flat(Iterable \a) { a.flat }
  1296. sub cache(+@l) { @l }
  1297. proto sub infix:<xx>(|) { * }
  1298. multi sub infix:<xx>() { Failure.new("No zero-arg meaning for infix:<xx>") }
  1299. multi sub infix:<xx>(Mu \x) { x }
  1300. multi sub infix:<xx>(&x, Num() $n) {
  1301. infix:<xx>(&x, $n == Inf ?? Whatever !! $n.Int);
  1302. }
  1303. multi sub infix:<xx>(&x, Whatever) {
  1304. Seq.new(Rakudo::Iterator.Callable-xx-Whatever(&x))
  1305. }
  1306. multi sub infix:<xx>(&x, Int $n) {
  1307. my int $todo = $n + 1;
  1308. my Mu $pulled;
  1309. my Mu $list := nqp::create(IterationBuffer);
  1310. nqp::while(
  1311. nqp::isgt_i($todo = nqp::sub_i($todo,1),0),
  1312. nqp::if(
  1313. nqp::istype(($pulled := &x.()),Slip),
  1314. (nqp::push($list,$_) for $pulled),
  1315. nqp::if(
  1316. nqp::istype($pulled,Seq),
  1317. nqp::push($list,$pulled.cache),
  1318. nqp::push($list,nqp::decont($pulled))
  1319. )
  1320. )
  1321. );
  1322. Seq.new(Rakudo::Iterator.ReifiedList($list))
  1323. }
  1324. multi sub infix:<xx>(Mu \x, Num() $n) {
  1325. Seq.new(nqp::if(
  1326. $n == Inf,
  1327. Rakudo::Iterator.UnendingValue(x),
  1328. Rakudo::Iterator.OneValueTimes(x,$n.Int)
  1329. ))
  1330. }
  1331. multi sub infix:<xx>(Mu \x, Whatever) {
  1332. Seq.new(Rakudo::Iterator.UnendingValue(x))
  1333. }
  1334. multi sub infix:<xx>(Mu \x, Int:D $n) is pure {
  1335. Seq.new(Rakudo::Iterator.OneValueTimes(x,$n))
  1336. }
  1337. proto sub reverse(|) { * }
  1338. multi sub reverse(@a) { @a.reverse }
  1339. multi sub reverse(+@a) { @a.reverse }
  1340. sub rotate(@a, Int $n = 1) { @a.rotate($n) }
  1341. sub prefix:<|>(\x) { x.Slip }
  1342. multi sub infix:<cmp>(@a, @b) {
  1343. (@a Zcmp @b).first(&prefix:<?>) || @a <=> @b
  1344. }
  1345. proto sub infix:<X>(|) is pure {*}
  1346. multi sub infix:<X>(+lol, :&with!) {
  1347. Seq.new(Rakudo::Iterator.CrossIterablesOp(lol,&with))
  1348. }
  1349. multi sub infix:<X>(+lol) {
  1350. Seq.new(Rakudo::Iterator.CrossIterablesOp(lol,&infix:<,>))
  1351. }
  1352. my constant &cross := &infix:<X>;
  1353. proto sub infix:<Z>(|) is pure {*}
  1354. multi sub infix:<Z>(+lol, :&with!) {
  1355. Seq.new(Rakudo::Iterator.ZipIterablesOp(lol,&with))
  1356. }
  1357. multi sub infix:<Z>(+lol) {
  1358. Seq.new(Rakudo::Iterator.ZipIterables(lol))
  1359. }
  1360. my constant &zip := &infix:<Z>;
  1361. sub roundrobin(+lol) {
  1362. Seq.new(Rakudo::Iterator.RoundrobinIterables(lol))
  1363. }