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