1. # This class contains generally usable methods creating Iterators.
  2. # There are two reasons for having this in a separate class:
  3. #
  4. # 1. Nice to have a separate file for similar stuff. Rakudo::Internals
  5. # has become a hodgepodge of stuff of late.
  6. # 2. Improve readability/searchability of code using these iterators, as
  7. # many already have a long name, and having them prefixed with the more
  8. # general Rakudo::Internals in the code, as opposed for the definite
  9. # Rakudo::Iterator, feels better.
  10. class Rakudo::Iterator {
  11. my $empty := nqp::list; # an empty list for nqp::splice
  12. #-------------------------------------------------------------------------------
  13. # Roles that are used by iterators in the rest of the core settings, in
  14. # alphabetical order for easier perusal.
  15. # Generic role for iterating over a Blob / Buf. You need to
  16. # supply at least a .pull-one. Takes a Blob / Buf as the only
  17. # parameter to .new.
  18. our role Blobby does Iterator {
  19. has $!blob;
  20. has Int $!i; # sadly, this can not be a native int yet :-(
  21. method SET-SELF(\blob) {
  22. nqp::stmts( # something to iterator over
  23. ($!blob := blob),
  24. ($!i = -1),
  25. self
  26. )
  27. }
  28. method new(\blob) {
  29. nqp::if(
  30. nqp::isgt_i(nqp::elems(blob),0),
  31. nqp::create(self).SET-SELF(blob),
  32. Rakudo::Iterator.Empty # nothing to iterate
  33. )
  34. }
  35. # We can provide a generic push-all to the iterator as the
  36. # result of a push-all is always immutable, so we can use
  37. # the atpos_i here in both cases.
  38. method push-all($target --> IterationEnd) {
  39. nqp::stmts(
  40. (my $blob := $!blob), # attribute access is slower
  41. (my int $elems = nqp::elems($blob)),
  42. (my int $i = $!i),
  43. nqp::while(
  44. nqp::islt_i(($i = nqp::add_i($i,1)),$elems),
  45. $target.push(nqp::atpos_i($blob,$i))
  46. )
  47. )
  48. }
  49. method count-only() { nqp::p6box_i(nqp::elems($!blob)) }
  50. method sink-all(--> IterationEnd) { $!i = nqp::elems($!blob) }
  51. }
  52. # Generic role for iterating over a Map / Hash. You must
  53. # at least provide your own .pull-one. Takes a Map / Hash
  54. # as the only parameter to .new.
  55. our role Mappy does Iterator {
  56. has $!storage;
  57. has $!iter;
  58. method SET-SELF(\hash) {
  59. nqp::stmts(
  60. ($!storage := nqp::getattr(hash,Map,'$!storage')),
  61. ($!iter := nqp::iterator($!storage)),
  62. self
  63. )
  64. }
  65. method new(\hash) {
  66. nqp::if(
  67. (nqp::getattr(hash,Map,'$!storage').DEFINITE
  68. && nqp::elems(nqp::getattr(hash,Map,'$!storage'))),
  69. nqp::create(self).SET-SELF(hash),
  70. Rakudo::Iterator.Empty # nothing to iterate
  71. )
  72. }
  73. method skip-one() { nqp::if($!iter,nqp::stmts(nqp::shift($!iter),1)) }
  74. method count-only() { nqp::p6box_i(nqp::elems($!storage)) }
  75. method bool-only(--> True) { }
  76. method sink-all(--> IterationEnd) { $!iter := nqp::null }
  77. }
  78. # Generic role for iterating over a >1 dimensional shaped list
  79. # for its lowest branches. The default .new method takes a List
  80. # to iterate over. A consuming class needs to provide a .process
  81. # method, which will be called with each iteration with the
  82. # $!indices attribute set to the coordinates of the branch being
  83. # iterated for this time (with the highest element index set to 0).
  84. # Consuming class can optionally provide a .done method that will
  85. # be called just before the iterator returns IterationEnd.
  86. our role ShapeBranch does Iterator {
  87. has $!dims;
  88. has $!indices;
  89. has Mu $!list;
  90. has int $!maxdim;
  91. has int $!maxind;
  92. has int $!level;
  93. # Every time process() gets called, the following attributes are set:
  94. # $!indices a list_i with current position, with the highest elem 0
  95. # $!level level at which exhaustion happened
  96. # $!dims a list_i with dimensions
  97. # $!maxdim maximum element number in $!dims
  98. # $!maxind maximum element number in lowest level list
  99. method process { ... } # consumer needs to supply a .process
  100. method done(--> Nil) { } # by default no action at end
  101. method dims() { # HLL version of $!dims
  102. nqp::stmts(
  103. (my $buffer :=
  104. nqp::setelems(nqp::create(IterationBuffer),nqp::elems($!dims))),
  105. (my int $i = -1),
  106. nqp::while( # convert list_i to list
  107. nqp::isle_i(($i = nqp::add_i($i,1)),$!maxdim),
  108. nqp::bindpos($buffer,$i,nqp::atpos_i($!dims,$i))
  109. ),
  110. nqp::p6bindattrinvres(nqp::create(List),List,'$!reified',$buffer)
  111. )
  112. }
  113. method SET-SELF(Mu \list) {
  114. nqp::stmts(
  115. nqp::if(
  116. nqp::istype(list,List),
  117. nqp::stmts( # List like
  118. ($!list := nqp::getattr(list,List,'$!reified')),
  119. (my $shape := list.shape),
  120. (my int $dims = $shape.elems), # reifies
  121. ($!dims := nqp::setelems(nqp::list_i,$dims)),
  122. (my int $i = -1),
  123. nqp::while(
  124. nqp::islt_i(($i = nqp::add_i($i,1)),$dims),
  125. nqp::bindpos_i($!dims,$i,
  126. nqp::atpos(nqp::getattr($shape,List,'$!reified'),$i))
  127. )
  128. ),
  129. ($dims = nqp::elems($!dims := nqp::dimensions($!list := list)))
  130. ),
  131. ($!indices := nqp::setelems(nqp::list_i,$dims)),
  132. ($!maxdim = nqp::sub_i($dims,1)),
  133. ($!maxind = nqp::sub_i(nqp::atpos_i($!dims,$!maxdim),1)),
  134. self
  135. )
  136. }
  137. method new(Mu \list) { nqp::create(self).SET-SELF(list) }
  138. method pull-one() is raw {
  139. nqp::if(
  140. nqp::isge_i($!level,0),
  141. nqp::stmts( # still iterating
  142. (my $result := self.process), # do the processing
  143. (my int $level = $!maxdim),
  144. nqp::until( # update indices
  145. nqp::islt_i( # exhausted ??
  146. ($level = nqp::sub_i($level,1)),0) # next level
  147. || nqp::stmts(
  148. nqp::bindpos_i($!indices,nqp::add_i($level,1),0), # reset
  149. nqp::islt_i(
  150. nqp::bindpos_i($!indices,$level, # increment this level
  151. nqp::add_i(nqp::atpos_i($!indices,$level),1)),
  152. nqp::atpos_i($!dims,$level) # out of range?
  153. ),
  154. ),
  155. nqp::null
  156. ),
  157. ($!level = $level), # set level for next call
  158. $result # what we found
  159. ),
  160. nqp::stmts(
  161. nqp::if(
  162. nqp::iseq_i($!level,-1),
  163. nqp::stmts( # first time telling we're done
  164. self.done, # notify we're done
  165. ($!level = -2) # do this only once
  166. )
  167. ),
  168. IterationEnd # done iterating
  169. )
  170. )
  171. }
  172. }
  173. # Generic role for iterating over a >1 dimensional shaped list
  174. # for its values (leaves). The default .new method takes a List
  175. # to iterate over. A consuming class needs to provide a .result
  176. # method, which will be called with each iteration with the
  177. # $!indices attribute set to the coordinates of the element being
  178. # iterated for this time. In some cases, the iterator is iterated
  179. # over for the side-effects in .result only. Which is why this
  180. # role supplies an optimized .sink-all.
  181. our role ShapeLeaf does Iterator {
  182. has $!dims;
  183. has $!indices;
  184. has Mu $!list;
  185. has int $!maxdim;
  186. has int $!max;
  187. # Every time .result gets called, the following attributes are set:
  188. # $!indices a list_i with current coordinate
  189. # $!dims a list_i with dimensions
  190. # $!maxdim maximum element number in $!dims
  191. method result { ... } # consumer needs to supply a .result
  192. method indices() { # HLL version of $!indices
  193. nqp::stmts(
  194. (my $result := nqp::setelems(nqp::list,nqp::elems($!indices))),
  195. (my int $i = -1),
  196. nqp::while( # convert list_i to list
  197. nqp::isle_i(($i = nqp::add_i($i,1)),$!maxdim),
  198. nqp::bindpos($result,$i,nqp::atpos_i($!indices,$i))
  199. ),
  200. $result
  201. )
  202. }
  203. method SET-SELF(Mu \list) {
  204. nqp::stmts(
  205. nqp::if(
  206. nqp::istype(list,List),
  207. nqp::stmts( # List like
  208. ($!list := nqp::getattr(list,List,'$!reified')),
  209. (my $shape := list.shape),
  210. (my int $dims = $shape.elems), # reifies
  211. ($!dims := nqp::setelems(nqp::list_i,$dims)),
  212. (my int $i = -1),
  213. nqp::while(
  214. nqp::islt_i(($i = nqp::add_i($i,1)),$dims),
  215. nqp::bindpos_i($!dims,$i,
  216. nqp::atpos(nqp::getattr($shape,List,'$!reified'),$i))
  217. )
  218. ),
  219. ($dims = nqp::elems($!dims := nqp::dimensions($!list := list)))
  220. ),
  221. ($!indices := nqp::setelems(nqp::list_i,$dims)),
  222. ($!maxdim = nqp::sub_i($dims,1)),
  223. ($!max = nqp::atpos_i($!dims,$!maxdim)),
  224. self
  225. )
  226. }
  227. method new(Mu \list) { nqp::create(self).SET-SELF(list) }
  228. method pull-one() is raw {
  229. nqp::if(
  230. $!indices,
  231. nqp::stmts( # still iterating
  232. (my $result := self.result), # process
  233. nqp::if(
  234. nqp::islt_i(
  235. (my int $i =
  236. nqp::add_i(nqp::atpos_i($!indices,$!maxdim),1)),
  237. $!max
  238. ),
  239. nqp::bindpos_i($!indices,$!maxdim,$i), # ready for next
  240. nqp::stmts( # done for now
  241. (my int $level = $!maxdim),
  242. nqp::until( # update indices
  243. nqp::islt_i( # exhausted ??
  244. ($level = nqp::sub_i($level,1)),0)
  245. || nqp::stmts(
  246. nqp::bindpos_i($!indices,nqp::add_i($level,1),0),
  247. nqp::islt_i(
  248. nqp::bindpos_i($!indices,$level,
  249. nqp::add_i(nqp::atpos_i($!indices,$level),1)),
  250. nqp::atpos_i($!dims,$level)
  251. ),
  252. ),
  253. nqp::null
  254. ),
  255. nqp::if(
  256. nqp::islt_i($level,0),
  257. $!indices := nqp::null # done next time
  258. )
  259. )
  260. ),
  261. $result # what we found
  262. ),
  263. IterationEnd # done now
  264. )
  265. }
  266. method push-all($target --> IterationEnd) {
  267. nqp::while(
  268. $!indices,
  269. nqp::stmts( # still iterating
  270. (my int $i = nqp::atpos_i($!indices,$!maxdim)),
  271. nqp::while(
  272. nqp::isle_i(($i = nqp::add_i($i,1)),$!max),
  273. nqp::stmts(
  274. $target.push(self.result), # process
  275. nqp::bindpos_i($!indices,$!maxdim,$i), # ready for next
  276. )
  277. ),
  278. (my int $level = $!maxdim), # done for now
  279. nqp::until( # update indices
  280. nqp::islt_i( # exhausted ??
  281. ($level = nqp::sub_i($level,1)),0)
  282. || nqp::stmts(
  283. nqp::bindpos_i($!indices,nqp::add_i($level,1),0),
  284. nqp::islt_i(
  285. nqp::bindpos_i($!indices,$level,
  286. nqp::add_i(nqp::atpos_i($!indices,$level),1)),
  287. nqp::atpos_i($!dims,$level)
  288. ),
  289. ),
  290. nqp::null
  291. ),
  292. nqp::if(
  293. nqp::islt_i($level,0),
  294. $!indices := nqp::null # done
  295. )
  296. )
  297. )
  298. }
  299. method sink-all(--> IterationEnd) {
  300. nqp::while(
  301. $!indices,
  302. nqp::stmts( # still iterating
  303. (my int $i = nqp::atpos_i($!indices,$!maxdim)),
  304. nqp::while(
  305. nqp::isle_i(($i = nqp::add_i($i,1)),$!max),
  306. nqp::stmts(
  307. self.result, # process
  308. nqp::bindpos_i($!indices,$!maxdim,$i), # ready for next
  309. )
  310. ),
  311. (my int $level = $!maxdim), # done for now
  312. nqp::until( # update indices
  313. nqp::islt_i( # exhausted ??
  314. ($level = nqp::sub_i($level,1)),0)
  315. || nqp::stmts(
  316. nqp::bindpos_i($!indices,nqp::add_i($level,1),0),
  317. nqp::islt_i(
  318. nqp::bindpos_i($!indices,$level,
  319. nqp::add_i(nqp::atpos_i($!indices,$level),1)),
  320. nqp::atpos_i($!dims,$level)
  321. ),
  322. ),
  323. nqp::null
  324. ),
  325. nqp::if(
  326. nqp::islt_i($level,0),
  327. $!indices := nqp::null # done
  328. )
  329. )
  330. )
  331. }
  332. }
  333. #-------------------------------------------------------------------------------
  334. # Methods that generate an Iterator (in alphabetical order)
  335. # Return an iterator that will generate a pair with the value as the
  336. # key and as value the key of the given iterator, basically the
  337. # .antipairs functionality on 1 dimensional lists.
  338. method AntiPair(\iterator) {
  339. class :: does Iterator {
  340. has Mu $!iter;
  341. has int $!key;
  342. method !SET-SELF(\iter) { $!iter := iter; $!key = -1; self }
  343. method new(\iter) { nqp::create(self)!SET-SELF(iter) }
  344. method pull-one() is raw {
  345. nqp::if(
  346. nqp::eqaddr((my $pulled := $!iter.pull-one),IterationEnd),
  347. IterationEnd,
  348. Pair.new($pulled,+($!key = nqp::add_i($!key,1)))
  349. )
  350. }
  351. method push-all($target --> IterationEnd) {
  352. my $pulled;
  353. my int $key = -1;
  354. nqp::until(
  355. nqp::eqaddr(($pulled := $!iter.pull-one),IterationEnd),
  356. $target.push(Pair.new($pulled,+($key = nqp::add_i($key,1))))
  357. )
  358. }
  359. }.new(iterator)
  360. }
  361. # Return an iterator that batches the given source iterator in
  362. # batches of the given size. The third parameter indicates whether
  363. # a partial batch should be returned when the source iterator has
  364. # exhausted. The returned iterator is as lazy as the source iterator.
  365. method Batch(\iterator,\size,\partial) {
  366. class :: does Iterator {
  367. has $!iterator;
  368. has int $!size;
  369. has int $!complete;
  370. has int $!is-exhausted = 0;
  371. method !SET-SELF(\iterator,\size,\partial) {
  372. nqp::stmts(
  373. ($!iterator := iterator),
  374. nqp::if(
  375. nqp::istype(size,Whatever),
  376. ($!size = -1), # set to never stop and ok partial
  377. nqp::if(
  378. size < 1,
  379. X::OutOfRange.new(
  380. what => "Batching sublist length is",
  381. got => size,
  382. range => "1..^Inf",
  383. ).throw,
  384. nqp::if(
  385. (nqp::istype(size,Int)
  386. && nqp::isbig_I(nqp::decont(size)))
  387. || size == Inf,
  388. ($!size = -1), # set to never stop and ok partial
  389. nqp::stmts(
  390. ($!size = size),
  391. ($!complete = !partial),
  392. )
  393. )
  394. )
  395. ),
  396. self
  397. )
  398. }
  399. method new(\it,\si,\pa) { nqp::create(self)!SET-SELF(it,si,pa) }
  400. method pull-one() is raw {
  401. nqp::if($!is-exhausted,
  402. IterationEnd,
  403. nqp::stmts(
  404. (my $reified := nqp::create(IterationBuffer)),
  405. nqp::until(
  406. nqp::iseq_i(nqp::elems($reified),$!size)
  407. || nqp::eqaddr(
  408. (my $pulled := $!iterator.pull-one),
  409. IterationEnd
  410. ),
  411. nqp::push($reified,$pulled)
  412. ),
  413. nqp::if(
  414. nqp::eqaddr($pulled,IterationEnd)
  415. && ($!is-exhausted = 1) # set the flag
  416. && ($!complete || nqp::not_i(nqp::elems($reified))),
  417. IterationEnd,
  418. nqp::p6bindattrinvres(
  419. nqp::create(List),List,'$!reified',$reified
  420. )
  421. )
  422. )
  423. )
  424. }
  425. method is-lazy() { $!iterator.is-lazy }
  426. }.new(iterator,size,partial)
  427. }
  428. # Return an iterator for a given Callable. The Callable is supposed
  429. # to return a value for the iterator, or IterationEnd to indicate the
  430. # data from the Callable is exhausted. No checks for Slips are done,
  431. # so they will be passed on as is. Also optionally takes a flag to
  432. # mark the iterator as lazy or not: default is False (not lazy)
  433. proto method Callable(|) { * }
  434. multi method Callable(&callable) {
  435. class :: does Iterator {
  436. has &!callable;
  437. method new(&callable) {
  438. nqp::p6bindattrinvres(
  439. nqp::create(self),self,'&!callable',&callable)
  440. }
  441. method pull-one() is raw { &!callable() }
  442. }.new(&callable)
  443. }
  444. multi method Callable(&callable, Bool() $lazy) {
  445. nqp::if(
  446. $lazy,
  447. class :: does Iterator {
  448. has &!callable;
  449. method new(&callable) {
  450. nqp::p6bindattrinvres(
  451. nqp::create(self),self,'&!callable',&callable)
  452. }
  453. method pull-one() is raw { &!callable() }
  454. method is-lazy(--> True) { }
  455. }.new(&callable),
  456. Rakudo::Iterator.Callable(&callable)
  457. )
  458. }
  459. # Return an iterator for the "thunk xx 42" functionality.
  460. method Callable-xx-Times(&code, Int:D \times) {
  461. class :: does Iterator {
  462. has @!slipped;
  463. has $!code;
  464. has $!times;
  465. method !SET-SELF(\code,\times) {
  466. nqp::stmts(
  467. ($!code := code),
  468. ($!times = times),
  469. self
  470. )
  471. }
  472. method new(\code,\times) {
  473. nqp::if(
  474. times > 0,
  475. nqp::create(self)!SET-SELF(code,times),
  476. Rakudo::Iterator.Empty
  477. )
  478. }
  479. method pull-one() {
  480. nqp::if(
  481. @!slipped,
  482. @!slipped.shift,
  483. nqp::if(
  484. $!times > 0,
  485. nqp::stmts(
  486. --$!times, # consumed a value
  487. nqp::if(
  488. nqp::istype((my $pulled := $!code()),Slip),
  489. nqp::if(
  490. (@!slipped = $pulled),
  491. @!slipped.shift,
  492. IterationEnd
  493. ),
  494. nqp::if(
  495. nqp::istype($pulled,Seq),
  496. $pulled.cache,
  497. $pulled
  498. )
  499. )
  500. ),
  501. IterationEnd
  502. )
  503. )
  504. }
  505. }.new(&code,times)
  506. }
  507. # Return an iterator for the "thunk xx *" functionality.
  508. method Callable-xx-Whatever(&code) {
  509. class :: does Iterator {
  510. has @!slipped;
  511. has $!code;
  512. method new(\code) {
  513. nqp::p6bindattrinvres(nqp::create(self),self,'$!code',code)
  514. }
  515. method pull-one() {
  516. nqp::if(
  517. @!slipped,
  518. @!slipped.shift,
  519. nqp::if(
  520. nqp::istype((my $pulled := $!code()),Slip),
  521. nqp::if(
  522. (@!slipped = $pulled),
  523. @!slipped.shift,
  524. IterationEnd
  525. ),
  526. nqp::if(
  527. nqp::istype($pulled,Seq),
  528. $pulled.cache,
  529. $pulled
  530. )
  531. )
  532. )
  533. }
  534. method is-lazy(--> True) { }
  535. }.new(&code)
  536. }
  537. # Return an iterator for a range of 0..^N with a number of elements.
  538. # The third parameter indicates whether an IterationBuffer should be
  539. # returned (1) for each combinatin, or a fully reified List (0).
  540. # Has a highly optimized count-only, for those cases when one is only
  541. # interested in the number of combinations, rather than the actual
  542. # combinations. The workhorse of combinations().
  543. method Combinations($n, $k, int $b) {
  544. nqp::if(
  545. $n > 0 && nqp::isbig_I(nqp::decont($n)), # must be HLL comparison
  546. X::OutOfRange.new(
  547. :what("First parameter"),
  548. :got($n),
  549. :range("-Inf^..{$*KERNEL.bits == 32 ?? 2**28-1 !! 2**31-1}")
  550. ).throw,
  551. nqp::if(
  552. # k = 0 → can pick just 1 combination (empty list); return ((),)
  553. $k == 0, # Must be HLL comparison
  554. Rakudo::Iterator.OneValue(
  555. nqp::create(nqp::if($b,IterationBuffer,List))
  556. ),
  557. nqp::if(
  558. # n < 1 → we have an empty list to pick from
  559. # n < k → not enough items to pick combination of k items
  560. $n < 1 || $n < $k || $k < 0, # must be HLL comparisons
  561. Rakudo::Iterator.Empty, # nothing to return
  562. class :: does Iterator {
  563. has int $!n;
  564. has int $!k;
  565. has int $!b;
  566. has Mu $!stack;
  567. has Mu $!combination;
  568. method !SET-SELF(\n,\k,\b) {
  569. nqp::stmts(
  570. ($!n = n),
  571. ($!k = k),
  572. ($!b = b),
  573. ($!stack := nqp::list_i(0)),
  574. ($!combination := nqp::create(IterationBuffer)),
  575. self
  576. )
  577. }
  578. method new(\n,\k,\b) { nqp::create(self)!SET-SELF(n,k,b) }
  579. method pull-one() {
  580. nqp::stmts(
  581. (my int $n = $!n), # lexicals faster
  582. (my int $k = $!k),
  583. (my int $running = 1),
  584. nqp::while(
  585. ($running && (my int $elems = nqp::elems($!stack))),
  586. nqp::stmts(
  587. (my int $index = nqp::sub_i($elems,1)),
  588. (my int $value = nqp::pop_i($!stack)),
  589. nqp::while(
  590. (nqp::islt_i($value,$n)
  591. && nqp::islt_i($index,$k)),
  592. nqp::stmts(
  593. nqp::bindpos($!combination,
  594. $index,nqp::clone($value)),
  595. ($index = nqp::add_i($index,1)),
  596. ($value = nqp::add_i($value,1)),
  597. nqp::push_i($!stack,$value)
  598. )
  599. ),
  600. ($running = nqp::isne_i($index,$k)),
  601. )
  602. ),
  603. nqp::if(
  604. nqp::iseq_i($index,$k),
  605. nqp::if(
  606. $b,
  607. nqp::clone($!combination),
  608. nqp::p6bindattrinvres(
  609. nqp::create(List),List,'$!reified',
  610. nqp::clone($!combination)
  611. )
  612. ),
  613. IterationEnd
  614. )
  615. )
  616. }
  617. method count-only {
  618. ([*] ($!n ... 0) Z/ 1 .. min($!n - $!k, $!k)).Int
  619. }
  620. method bool-only(--> True) { }
  621. }.new($n,$k,$b)
  622. )
  623. )
  624. )
  625. }
  626. # Return an iterator that will cross the given iterables (with &[,])
  627. # Basically the functionality of @a X @b
  628. method CrossIterables(@iterables) {
  629. nqp::if(
  630. nqp::isgt_i((my int $n = @iterables.elems),1), # reifies
  631. # actually need to do some crossing (probably)
  632. class :: does Iterator {
  633. has $!iterators; # iterator per iterable, if any
  634. has $!reifieds; # cached values (either complete, or so far)
  635. has $!indices; # indices of virtual matrix of crossed values
  636. has $!next; # IterationBuffer with next values to return
  637. has int $!lazy; # whether the outer iterator is lazy
  638. has int $!top; # index of top reified/iterator
  639. method !SET-SELF(\iterables) {
  640. nqp::stmts(
  641. (my $iterables := nqp::getattr(iterables,List,'$!reified')),
  642. (my int $elems = nqp::elems($iterables)),
  643. ($!iterators := nqp::setelems(nqp::list,$elems)),
  644. ($!reifieds := nqp::setelems(nqp::list,$elems)),
  645. ($!next :=
  646. nqp::setelems(nqp::create(IterationBuffer),$elems)),
  647. # loop over all iterables
  648. (my int $i = -1),
  649. nqp::while(
  650. nqp::islt_i(($i = nqp::add_i($i,1)),$elems),
  651. # set up initial value of index $i with...
  652. nqp::bindpos($!next,$i,nqp::if(
  653. nqp::iscont(my $elem := nqp::atpos($iterables,$i))
  654. || nqp::not_i(nqp::istype($elem,Iterable)),
  655. # single value same as reified list of 1
  656. nqp::bindpos(
  657. nqp::bindpos($!reifieds,$i,nqp::list),
  658. 0,
  659. $elem
  660. ),
  661. # something more elaborate
  662. nqp::if(
  663. nqp::istype($elem,List)
  664. && nqp::not_i(
  665. nqp::getattr($elem,List,'$!todo').DEFINITE),
  666. # it's a List, may have a reified we can use directly
  667. nqp::if(
  668. nqp::isnull(
  669. $elem := nqp::getattr($elem,List,'$!reified'))
  670. || nqp::iseq_i(nqp::elems($elem),0),
  671. # cross with an empty list is always an empty list
  672. (return Rakudo::Iterator.Empty),
  673. # use the available reified directly
  674. nqp::stmts(
  675. nqp::bindpos($!reifieds,$i,$elem),
  676. nqp::atpos($elem,0)
  677. )
  678. ),
  679. # need to set up an iterator
  680. nqp::stmts(
  681. nqp::if($elem.is-lazy,($!lazy = 1)),
  682. nqp::if(
  683. nqp::eqaddr(
  684. (my $pulled :=
  685. ($elem := $elem.iterator).pull-one),
  686. IterationEnd
  687. ),
  688. # cross with an empty list is an empty list
  689. (return Rakudo::Iterator.Empty),
  690. # set up the iterator stuff
  691. nqp::stmts(
  692. nqp::bindpos($!iterators,$i,$elem),
  693. nqp::bindpos($!reifieds,$i,nqp::list($pulled)),
  694. $pulled
  695. )
  696. )
  697. )
  698. )
  699. ))
  700. ),
  701. # indices start with 0 xx $elems
  702. ($!indices := nqp::setelems(nqp::list_i,$elems)),
  703. ($!top = nqp::sub_i($elems,1)),
  704. self
  705. )
  706. }
  707. method new(\iterables) { nqp::create(self)!SET-SELF(iterables) }
  708. method pull-one() {
  709. nqp::if(
  710. nqp::isnull($!next),
  711. IterationEnd,
  712. nqp::stmts(
  713. # set up result of this pull
  714. (my $result := nqp::p6bindattrinvres(
  715. nqp::create(List),List,'$!reified',nqp::clone($!next))),
  716. # start working on next result
  717. nqp::unless(
  718. nqp::isnull(nqp::atpos($!iterators,$!top)),
  719. # top level is still iterator, fetch
  720. nqp::if(
  721. nqp::eqaddr(
  722. (my $pulled :=
  723. nqp::atpos($!iterators,$!top).pull-one),
  724. IterationEnd
  725. ),
  726. # iterator no more
  727. nqp::bindpos($!iterators,$!top,nqp::null),
  728. # push value, let normal reifier handler handle
  729. nqp::push(
  730. nqp::atpos($!reifieds,$!top),
  731. $pulled
  732. )
  733. )
  734. ),
  735. # no iterator, must use reified list
  736. nqp::if(
  737. nqp::islt_i(
  738. (my int $index =
  739. nqp::add_i(nqp::atpos_i($!indices,$!top),1)),
  740. nqp::elems(nqp::atpos($!reifieds,$!top))
  741. ),
  742. # within range, update next result and index
  743. nqp::bindpos($!next,$!top,
  744. nqp::atpos(
  745. nqp::atpos($!reifieds,$!top),
  746. nqp::bindpos_i($!indices,$!top,$index)
  747. )
  748. ),
  749. # need to update lower levels
  750. nqp::stmts(
  751. # update topmost value (go back to first)
  752. nqp::bindpos($!next,$!top,
  753. nqp::atpos(
  754. nqp::atpos($!reifieds,$!top),
  755. nqp::bindpos_i($!indices,$!top,0)
  756. )
  757. ),
  758. # until we're at the bottom
  759. (my int $level = $!top),
  760. nqp::while(
  761. nqp::isge_i(($level = nqp::sub_i($level,1)),0),
  762. nqp::if(
  763. nqp::isnull(nqp::atpos($!iterators,$level)),
  764. # can use reified list at this level
  765. nqp::if(
  766. nqp::islt_i(
  767. ($index = nqp::add_i(
  768. nqp::atpos_i($!indices,$level),1)),
  769. nqp::elems(nqp::atpos($!reifieds,$level))
  770. ),
  771. # within range, update next result and index
  772. nqp::stmts(
  773. nqp::bindpos($!next,$level,
  774. nqp::atpos(
  775. nqp::atpos($!reifieds,$level),
  776. nqp::bindpos_i($!indices,$level,$index)
  777. )
  778. ),
  779. ($level = -1) # done searching
  780. ),
  781. # reset this level
  782. nqp::bindpos($!next,$level,
  783. nqp::atpos(
  784. nqp::atpos($!reifieds,$level),
  785. nqp::bindpos_i($!indices,$level,0)
  786. )
  787. )
  788. ),
  789. # still an iterator at this level
  790. nqp::if(
  791. nqp::eqaddr(
  792. ($pulled :=
  793. nqp::atpos($!iterators,$level).pull-one),
  794. IterationEnd
  795. ),
  796. # exhausted iterator, reset to reified
  797. nqp::stmts(
  798. nqp::bindpos($!iterators,$level,nqp::null),
  799. nqp::bindpos($!next,$level,
  800. nqp::atpos(
  801. nqp::atpos($!reifieds,$level),
  802. nqp::bindpos_i($!indices,$level,0)
  803. )
  804. )
  805. ),
  806. # new value, add to reified, update indices
  807. nqp::stmts(
  808. nqp::bindpos(
  809. $!next,
  810. $level,
  811. nqp::bindpos(
  812. nqp::atpos($!reifieds,$level),
  813. nqp::bindpos_i(
  814. $!indices,
  815. $level,
  816. nqp::add_i(
  817. nqp::atpos_i($!indices,$level),
  818. 1
  819. )
  820. ),
  821. $pulled
  822. )
  823. ),
  824. ($level = -1) # done searching
  825. )
  826. )
  827. )
  828. ),
  829. nqp::if(
  830. nqp::iseq_i($level,-1),
  831. # was last iteration, free up everything now
  832. ($!next :=
  833. $!iterators := $!reifieds := $!indices :=
  834. nqp::null)
  835. )
  836. )
  837. ),
  838. $result
  839. )
  840. )
  841. }
  842. method is-lazy() { nqp::p6bool($!lazy) }
  843. }.new(@iterables),
  844. # simpler cases
  845. nqp::if(
  846. nqp::iseq_i($n,0),
  847. # nothing to cross, so return an empty list
  848. Rakudo::Iterator.Empty,
  849. # only 1 list to cross, which is the list itself
  850. nqp::atpos(nqp::getattr(@iterables,List,'$!reified'),0).iterator
  851. )
  852. )
  853. }
  854. # Return an iterator that will cross the given iterables and map
  855. # the result with the given mapper Callable. Basically the
  856. # functionality of @a Xop @b (with the op -> mapper functionality
  857. # to be supplied externally).
  858. method CrossIterablesMap(@iterables,&mapper) {
  859. nqp::if(
  860. nqp::isgt_i((my int $n = @iterables.elems),1), # reifies
  861. # actually need to do some crossing (probably)
  862. class :: does Iterator {
  863. has $!iterators; # iterator per iterable, if any
  864. has $!reifieds; # cached values (either complete, or so far)
  865. has $!indices; # indices of virtual matrix of crossed values
  866. has $!next; # IterationBuffer with next values to return
  867. has $!mapper; # Callable to do final result mapping
  868. has int $!lazy; # whether the outer iterator is lazy
  869. has int $!top; # index of top reified/iterator
  870. method !SET-SELF(\iterables,\mapper) {
  871. nqp::stmts(
  872. (my $iterables := nqp::getattr(iterables,List,'$!reified')),
  873. (my int $elems = nqp::elems($iterables)),
  874. ($!iterators := nqp::setelems(nqp::list,$elems)),
  875. ($!reifieds := nqp::setelems(nqp::list,$elems)),
  876. ($!next :=
  877. nqp::setelems(nqp::create(IterationBuffer),$elems)),
  878. # loop over all iterables
  879. (my int $i = -1),
  880. nqp::while(
  881. nqp::islt_i(($i = nqp::add_i($i,1)),$elems),
  882. # set up initial value of index $i with...
  883. nqp::bindpos($!next,$i,nqp::if(
  884. nqp::iscont(my $elem := nqp::atpos($iterables,$i))
  885. || nqp::not_i(nqp::istype($elem,Iterable)),
  886. # single value same as reified list of 1
  887. nqp::bindpos(
  888. nqp::bindpos($!reifieds,$i,nqp::list),
  889. 0,
  890. $elem
  891. ),
  892. # something more elaborate
  893. nqp::if(
  894. nqp::istype($elem,List)
  895. && nqp::not_i(
  896. nqp::getattr($elem,List,'$!todo').DEFINITE),
  897. # it's a List, may have a reified we can use directly
  898. nqp::if(
  899. nqp::isnull(
  900. $elem := nqp::getattr($elem,List,'$!reified'))
  901. || nqp::iseq_i(nqp::elems($elem),0),
  902. # cross with an empty list is always an empty list
  903. (return Rakudo::Iterator.Empty),
  904. # use the available reified directly
  905. nqp::stmts(
  906. nqp::bindpos($!reifieds,$i,$elem),
  907. nqp::atpos($elem,0)
  908. )
  909. ),
  910. # need to set up an iterator
  911. nqp::stmts(
  912. nqp::if($elem.is-lazy,($!lazy = 1)),
  913. nqp::if(
  914. nqp::eqaddr(
  915. (my $pulled :=
  916. ($elem := $elem.iterator).pull-one),
  917. IterationEnd
  918. ),
  919. # cross with an empty list is an empty list
  920. (return Rakudo::Iterator.Empty),
  921. # set up the iterator stuff
  922. nqp::stmts(
  923. nqp::bindpos($!iterators,$i,$elem),
  924. nqp::bindpos($!reifieds,$i,nqp::list($pulled)),
  925. $pulled
  926. )
  927. )
  928. )
  929. )
  930. ))
  931. ),
  932. # indices start with 0 xx $elems
  933. ($!indices := nqp::setelems(nqp::list_i,$elems)),
  934. ($!top = nqp::sub_i($elems,1)),
  935. ($!mapper := mapper),
  936. self
  937. )
  938. }
  939. method new(\its,\map) { nqp::create(self)!SET-SELF(its,map) }
  940. method pull-one() {
  941. nqp::if(
  942. nqp::isnull($!next),
  943. IterationEnd,
  944. nqp::stmts(
  945. # set up result of this pull
  946. # we *MUST* clone here, because we cannot be sure
  947. # the mapper isn't going to throw the buffer away.
  948. (my $result := $!mapper(nqp::clone($!next))),
  949. # start working on next result
  950. nqp::unless(
  951. nqp::isnull(nqp::atpos($!iterators,$!top)),
  952. # top level is still iterator, fetch
  953. nqp::if(
  954. nqp::eqaddr(
  955. (my $pulled :=
  956. nqp::atpos($!iterators,$!top).pull-one),
  957. IterationEnd
  958. ),
  959. # iterator no more
  960. nqp::bindpos($!iterators,$!top,nqp::null),
  961. # push value, let normal reifier handler handle
  962. nqp::push(
  963. nqp::atpos($!reifieds,$!top),
  964. $pulled
  965. )
  966. )
  967. ),
  968. # no iterator, must use reified list
  969. nqp::if(
  970. nqp::islt_i(
  971. (my int $index =
  972. nqp::add_i(nqp::atpos_i($!indices,$!top),1)),
  973. nqp::elems(nqp::atpos($!reifieds,$!top))
  974. ),
  975. # within range, update next result and index
  976. nqp::bindpos($!next,$!top,
  977. nqp::atpos(
  978. nqp::atpos($!reifieds,$!top),
  979. nqp::bindpos_i($!indices,$!top,$index)
  980. )
  981. ),
  982. # need to update lower levels
  983. nqp::stmts(
  984. # update topmost value (go back to first)
  985. nqp::bindpos($!next,$!top,
  986. nqp::atpos(
  987. nqp::atpos($!reifieds,$!top),
  988. nqp::bindpos_i($!indices,$!top,0)
  989. )
  990. ),
  991. # until we're at the bottom
  992. (my int $level = $!top),
  993. nqp::while(
  994. nqp::isge_i(($level = nqp::sub_i($level,1)),0),
  995. nqp::if(
  996. nqp::isnull(nqp::atpos($!iterators,$level)),
  997. # can use reified list at this level
  998. nqp::if(
  999. nqp::islt_i(
  1000. ($index = nqp::add_i(
  1001. nqp::atpos_i($!indices,$level),1)),
  1002. nqp::elems(nqp::atpos($!reifieds,$level))
  1003. ),
  1004. # within range, update next result and index
  1005. nqp::stmts(
  1006. nqp::bindpos($!next,$level,
  1007. nqp::atpos(
  1008. nqp::atpos($!reifieds,$level),
  1009. nqp::bindpos_i($!indices,$level,$index)
  1010. )
  1011. ),
  1012. ($level = -1) # done searching
  1013. ),
  1014. # reset this level
  1015. nqp::bindpos($!next,$level,
  1016. nqp::atpos(
  1017. nqp::atpos($!reifieds,$level),
  1018. nqp::bindpos_i($!indices,$level,0)
  1019. )
  1020. )
  1021. ),
  1022. # still an iterator at this level
  1023. nqp::if(
  1024. nqp::eqaddr(
  1025. ($pulled :=
  1026. nqp::atpos($!iterators,$level).pull-one),
  1027. IterationEnd
  1028. ),
  1029. # exhausted iterator, reset to reified
  1030. nqp::stmts(
  1031. nqp::bindpos($!iterators,$level,nqp::null),
  1032. nqp::bindpos($!next,$level,
  1033. nqp::atpos(
  1034. nqp::atpos($!reifieds,$level),
  1035. nqp::bindpos_i($!indices,$level,0)
  1036. )
  1037. )
  1038. ),
  1039. # new value, add to reified, update indices
  1040. nqp::stmts(
  1041. nqp::bindpos(
  1042. $!next,
  1043. $level,
  1044. nqp::bindpos(
  1045. nqp::atpos($!reifieds,$level),
  1046. nqp::bindpos_i(
  1047. $!indices,
  1048. $level,
  1049. nqp::add_i(
  1050. nqp::atpos_i($!indices,$level),
  1051. 1
  1052. )
  1053. ),
  1054. $pulled
  1055. )
  1056. ),
  1057. ($level = -1) # done searching
  1058. )
  1059. )
  1060. )
  1061. ),
  1062. nqp::if(
  1063. nqp::iseq_i($level,-1),
  1064. # was last iteration, free up everything now
  1065. ($!next :=
  1066. $!iterators := $!reifieds := $!indices :=
  1067. nqp::null)
  1068. )
  1069. )
  1070. ),
  1071. $result
  1072. )
  1073. )
  1074. }
  1075. method is-lazy() { nqp::p6bool($!lazy) }
  1076. }.new(@iterables,&mapper),
  1077. # simpler cases
  1078. nqp::if(
  1079. nqp::iseq_i($n,0),
  1080. # nothing to cross, so return an empty list
  1081. Rakudo::Iterator.Empty,
  1082. # only 1 list to cross, which is the list itself
  1083. nqp::atpos(nqp::getattr(@iterables,List,'$!reified'),0).iterator
  1084. )
  1085. )
  1086. }
  1087. # Return an iterator that will cross the given iterables and operator.
  1088. # Basically the functionality of @a Z=> @b, with &[=>] being the op.
  1089. method CrossIterablesOp(@iterables,\op) {
  1090. nqp::if(
  1091. nqp::eqaddr(op,&infix:<,>),
  1092. Rakudo::Iterator.CrossIterables(@iterables),
  1093. Rakudo::Iterator.CrossIterablesMap(
  1094. @iterables,
  1095. Rakudo::Metaops.MapperForOp(op)
  1096. )
  1097. )
  1098. }
  1099. # Returns an iterator that handles all properties of a -while- with
  1100. # a condition. Takes a Callable to be considered the body of the loop,
  1101. # and a Callable for the condition..
  1102. method CStyleLoop(&body,&cond,&afterwards) {
  1103. class :: does SlippyIterator {
  1104. has &!body;
  1105. has &!cond;
  1106. has &!afterwards;
  1107. has int $!seen-first;
  1108. method !SET-SELF(\body,\cond,\afterwards) {
  1109. nqp::stmts(
  1110. (&!body := body),
  1111. (&!cond := cond),
  1112. (&!afterwards := afterwards),
  1113. self
  1114. )
  1115. }
  1116. method new(\body,\cond,\afterwards) {
  1117. nqp::create(self)!SET-SELF(body,cond,afterwards)
  1118. }
  1119. method pull-one() {
  1120. if $!slipping && nqp::not_i(
  1121. nqp::eqaddr((my $result := self.slip-one),IterationEnd)
  1122. ) {
  1123. $result
  1124. }
  1125. else {
  1126. nqp::stmts(
  1127. nqp::if(
  1128. $!seen-first,
  1129. &!afterwards(),
  1130. ($!seen-first = 1)
  1131. ),
  1132. nqp::if(
  1133. &!cond(),
  1134. nqp::stmts(
  1135. nqp::until(
  1136. (my int $stopped),
  1137. nqp::stmts(
  1138. ($stopped = 1),
  1139. nqp::handle(
  1140. nqp::if(
  1141. nqp::istype(($result := &!body()),Slip),
  1142. nqp::if(
  1143. nqp::eqaddr(
  1144. ($result := self.start-slip($result)),
  1145. IterationEnd
  1146. ),
  1147. nqp::stmts(
  1148. &!afterwards(),
  1149. ($stopped = nqp::if(&!cond(),0,1))
  1150. )
  1151. )
  1152. ),
  1153. 'NEXT', nqp::stmts(
  1154. &!afterwards(),
  1155. ($stopped = nqp::if(&!cond(),0,1))
  1156. ),
  1157. 'REDO', ($stopped = 0),
  1158. 'LAST', ($result := IterationEnd)
  1159. )
  1160. ),
  1161. :nohandler
  1162. ),
  1163. $result
  1164. ),
  1165. IterationEnd
  1166. )
  1167. )
  1168. }
  1169. }
  1170. }.new(&body,&cond,&afterwards)
  1171. }
  1172. # Create an iterator from a source iterator that will repeat the
  1173. # values of the source iterator indefinitely *unless* a Whatever
  1174. # was encountered, in which case it will repeat the last seen value
  1175. # indefinitely (even if the source iterator wasn't actually exhausted).
  1176. # Only if the source iterator did not produce any values at all, then
  1177. # the resulting iterator will not produce any either.
  1178. method DWIM(\source) {
  1179. class :: does Iterator {
  1180. has $!source;
  1181. has $!buffer;
  1182. has int $!ended;
  1183. has int $!whatever;
  1184. has int $!i;
  1185. method !SET-SELF(\source) {
  1186. $!source := source;
  1187. $!buffer := IterationBuffer.new;
  1188. self
  1189. }
  1190. method new(\source) { nqp::create(self)!SET-SELF(source) }
  1191. method pull-one() is raw {
  1192. nqp::if(
  1193. $!ended,
  1194. nqp::if( # source exhausted
  1195. $!whatever,
  1196. nqp::if( # seen a Whatever
  1197. nqp::elems($!buffer),
  1198. nqp::atpos($!buffer, # last value seen
  1199. nqp::sub_i(nqp::elems($!buffer),1)),
  1200. Nil # no last value seen
  1201. ),
  1202. nqp::atpos($!buffer, # not seen, so modulo repeat
  1203. nqp::mod_i(
  1204. nqp::sub_i(($!i = nqp::add_i($!i,1)),1),
  1205. nqp::elems($!buffer)
  1206. )
  1207. )
  1208. ),
  1209. nqp::if( # source not exhausted
  1210. nqp::eqaddr((my $value := $!source.pull-one),IterationEnd),
  1211. nqp::stmts( # exhausted now
  1212. ($!ended = 1),
  1213. nqp::if(
  1214. nqp::iseq_i(nqp::elems($!buffer),0),
  1215. IterationEnd, # nothing to repeat, done
  1216. self.pull-one # last or repeat
  1217. )
  1218. ),
  1219. nqp::if( # got a value
  1220. nqp::istype($value,Whatever),
  1221. nqp::stmts( # done, repeat last value
  1222. ($!whatever = $!ended = 1),
  1223. self.pull-one,
  1224. ),
  1225. nqp::stmts( # save / return value
  1226. $!buffer.push($value),
  1227. $value
  1228. )
  1229. )
  1230. )
  1231. )
  1232. }
  1233. # Is the source iterator considered exhausted?
  1234. method ended() { nqp::p6bool($!ended) }
  1235. # Eat the iterator trying to find out the number of elements
  1236. # produced by the iterator. Intended to provide information
  1237. # for error messages.
  1238. method count-elems() {
  1239. nqp::if(
  1240. $!ended,
  1241. nqp::elems($!buffer),
  1242. nqp::stmts(
  1243. (my int $elems = nqp::elems($!buffer)),
  1244. nqp::until(
  1245. nqp::eqaddr($!source.pull-one,IterationEnd),
  1246. $elems = nqp::add_i($elems,1)
  1247. ),
  1248. $elems
  1249. )
  1250. )
  1251. }
  1252. }.new(source)
  1253. }
  1254. # Returns a sentinel Iterator object that will never generate any value.
  1255. # Does not take a parameter.
  1256. method Empty() {
  1257. BEGIN class :: does Iterator {
  1258. method new() { nqp::create(self) }
  1259. method pull-one(--> IterationEnd) { }
  1260. method push-all($ --> IterationEnd) { }
  1261. method sink-all(--> IterationEnd) { }
  1262. method skip-one(--> 0) { }
  1263. method skip-at-least($ --> 0) { }
  1264. method count-only(--> 0) { }
  1265. method bool-only(--> False) { }
  1266. }.new
  1267. }
  1268. # Return an iterator that will cache a source iterator for the index
  1269. # values that the index iterator provides, from a given offest in the
  1270. # cached source iterator. Values from the index iterator below the
  1271. # offset, are considered to be illegal and will throw. Also takes an
  1272. # optional block to be called when an otherwise out-of-bounds index
  1273. # value is given by the index iterator: if not given, Nil will be
  1274. # returned for such index values.
  1275. method FromIndexes(\source,\indexes,\offset,&out?) {
  1276. class :: does Iterator {
  1277. has $!source;
  1278. has $!indexes;
  1279. has int $!offset;
  1280. has &!out;
  1281. has $!cache;
  1282. method !SET-SELF($!source,$!indexes,\offset,&!out) {
  1283. $!cache := nqp::setelems(nqp::list,$!offset = offset);
  1284. self
  1285. }
  1286. method new(\s,\i,\o,\out) { nqp::create(self)!SET-SELF(s,i,o,out) }
  1287. method pull-one() is raw {
  1288. nqp::if(
  1289. nqp::eqaddr((my $got := $!indexes.pull-one),IterationEnd),
  1290. IterationEnd,
  1291. nqp::if(
  1292. nqp::istype( # doesn't look like int
  1293. (my $number = +$got),Failure),
  1294. $number.throw,
  1295. nqp::if( # out of range
  1296. nqp::islt_i((my int $index = $number.Int),$!offset),
  1297. X::OutOfRange.new(:$got,:range("$!offset..^Inf")).throw,
  1298. nqp::if(
  1299. nqp::existspos($!cache,$index),
  1300. nqp::atpos($!cache,$index), # it's in the cache
  1301. nqp::if(
  1302. nqp::defined($!source),
  1303. nqp::stmts( # can still search it
  1304. nqp::until(
  1305. nqp::existspos($!cache,$index)
  1306. || nqp::eqaddr(
  1307. (my $pulled := $!source.pull-one),
  1308. IterationEnd
  1309. ),
  1310. nqp::push($!cache,$pulled)
  1311. ),
  1312. nqp::if(
  1313. nqp::eqaddr($pulled,IterationEnd),
  1314. nqp::stmts(
  1315. ($!source := Mu),
  1316. nqp::if(
  1317. $!indexes.is-lazy,
  1318. IterationEnd, # not going to be any more
  1319. nqp::stmts( # didn't find it
  1320. nqp::if(&out,out($index)),
  1321. Nil
  1322. )
  1323. )
  1324. ),
  1325. $pulled # found it
  1326. )
  1327. ),
  1328. nqp::stmts( # cannot be found
  1329. nqp::if(&out,out($index)),
  1330. Nil
  1331. )
  1332. )
  1333. )
  1334. )
  1335. )
  1336. )
  1337. }
  1338. method is-lazy() { $!source.is-lazy }
  1339. }.new(source,indexes,offset,&out)
  1340. }
  1341. # Return an iterator for the given low/high integer value (inclusive).
  1342. # Has dedicated .push-all for those cases one needs to fill a list
  1343. # with consecutive numbers quickly.
  1344. method IntRange(\from,\to) {
  1345. class :: does Iterator {
  1346. has int $!i;
  1347. has int $!last;
  1348. method !SET-SELF(int $i, int $last) {
  1349. nqp::stmts(
  1350. ($!i = nqp::sub_i($i,1)),
  1351. ($!last = $last),
  1352. self
  1353. )
  1354. }
  1355. method new(\f,\t) { nqp::create(self)!SET-SELF(f,t) }
  1356. method pull-one() {
  1357. nqp::if(
  1358. nqp::isle_i(($!i = nqp::add_i($!i,1)),$!last),
  1359. $!i,
  1360. IterationEnd
  1361. )
  1362. }
  1363. method push-all($target --> IterationEnd) {
  1364. nqp::stmts(
  1365. (my int $i = $!i), # lexicals are faster than attrs
  1366. (my int $last = $!last),
  1367. nqp::while(
  1368. nqp::isle_i(($i = nqp::add_i($i,1)),$last),
  1369. $target.push(nqp::p6box_i($i))
  1370. ),
  1371. ($!i = $i), # make sure pull-one ends
  1372. )
  1373. }
  1374. method count-only() { nqp::p6box_i(nqp::sub_i($!last,$!i)) }
  1375. method bool-only() { nqp::p6bool(nqp::isgt_i($!last,$!i)) }
  1376. method sink-all(--> IterationEnd) { $!i = $!last }
  1377. }.new(from,to)
  1378. }
  1379. # Return an iterator from a given iterator producing Pairs, in which
  1380. # each .value is checked for iterability: if Iterable, produce Pairs
  1381. # with the original key as its value, and key with the values produced
  1382. # by the Iterable. Otherwise, just produce an antipair.
  1383. method Invert(\iterator) {
  1384. class :: does Iterator {
  1385. has $!iterator; # source iterator
  1386. has $!value; # original key to repeat for Iterable
  1387. has $!slipper; # iterator if Iterable value in source
  1388. method new(\iterator) {
  1389. nqp::p6bindattrinvres(
  1390. nqp::create(self),self,'$!iterator',iterator)
  1391. }
  1392. method pull-one() {
  1393. nqp::if(
  1394. $!slipper, # we have a slipper
  1395. nqp::if(
  1396. nqp::eqaddr(
  1397. (my $pulled := $!slipper.pull-one),
  1398. IterationEnd
  1399. ),
  1400. nqp::stmts( # slipper exhausted
  1401. ($!slipper := nqp::null), # deny all knowledge
  1402. self.pull-one # rinse and repeat
  1403. ),
  1404. Pair.new($pulled,$!value) # not the end, slip it
  1405. ),
  1406. nqp::if( # no slipper
  1407. nqp::eqaddr(
  1408. ($pulled := nqp::decont($!iterator.pull-one)),
  1409. IterationEnd
  1410. ),
  1411. IterationEnd, # source exhausted
  1412. nqp::if( # still in business
  1413. nqp::istype($pulled,Pair),
  1414. nqp::if( # it's a Pair, whee!
  1415. nqp::istype(
  1416. (my $key := nqp::getattr($pulled,Pair,'$!value')),
  1417. Iterable
  1418. ),
  1419. nqp::stmts( # need to slip it!
  1420. ($!slipper := $key.iterator), # set up the slipper
  1421. ($!value := nqp::getattr($pulled,Pair,'$!key')),
  1422. self.pull-one # rinse and repeat
  1423. ),
  1424. Pair.new( # just needs swapping
  1425. $key,
  1426. nqp::getattr($pulled,Pair,'$!key')
  1427. )
  1428. ),
  1429. X::TypeCheck.new( # naughty, slap it!
  1430. operation => 'invert',
  1431. got => $pulled,
  1432. expected => Pair
  1433. ).throw
  1434. )
  1435. )
  1436. )
  1437. }
  1438. method is-lazy() { $!iterator.is-lazy }
  1439. method sink-all(--> IterationEnd) {
  1440. nqp::until(
  1441. nqp::eqaddr((my $pulled := $!iterator.pull-one),IterationEnd),
  1442. nqp::unless(
  1443. nqp::istype($pulled,Pair),
  1444. X::TypeCheck.new( # naughty, slap it!
  1445. operation => 'invert',
  1446. got => $pulled,
  1447. expected => Pair
  1448. ).throw
  1449. )
  1450. )
  1451. }
  1452. }.new(iterator)
  1453. }
  1454. # Return an iterator that will alternately generate an index value,
  1455. # and the value of the given iterator, basically the .kv functionality
  1456. # for 1 dimensional lists.
  1457. method KeyValue(\iterator) {
  1458. class :: does Iterator {
  1459. has Mu $!iter;
  1460. has Mu $!pulled;
  1461. has int $!on-key;
  1462. has int $!key;
  1463. method !SET-SELF(\iter) { $!iter := iter; $!key = -1; self }
  1464. method new(\iter) { nqp::create(self)!SET-SELF(iter) }
  1465. method pull-one() is raw {
  1466. nqp::if(
  1467. ($!on-key = nqp::not_i($!on-key)),
  1468. nqp::if(
  1469. nqp::eqaddr(
  1470. ($!pulled := $!iter.pull-one),IterationEnd
  1471. ),
  1472. IterationEnd,
  1473. nqp::p6box_i(($!key = nqp::add_i($!key,1))),
  1474. ),
  1475. $!pulled,
  1476. )
  1477. }
  1478. method push-all($target --> IterationEnd) {
  1479. my $pulled;
  1480. my int $key = -1;
  1481. nqp::until(
  1482. nqp::eqaddr(
  1483. ($pulled := $!iter.pull-one),
  1484. IterationEnd
  1485. ),
  1486. nqp::stmts(
  1487. $target.push(nqp::p6box_i(($key = nqp::add_i($key,1)))),
  1488. $target.push($pulled),
  1489. )
  1490. )
  1491. }
  1492. }.new(iterator)
  1493. }
  1494. # Create iterator for the last N values of a given iterator. Needs
  1495. # to specify the :action part of X::Cannot::Lazy in case the given
  1496. # iterator is lazy. Optionally returns an empty iterator if the
  1497. # given iterator produced fewer than N values.
  1498. method LastNValues(\iterator, \n, \action, $full = 0) {
  1499. class :: does Iterator {
  1500. has $!iterator;
  1501. has int $!size;
  1502. has int $!full;
  1503. has $!lastn;
  1504. has int $!todo;
  1505. has int $!index;
  1506. method !SET-SELF(\iterator, \size, \full) {
  1507. nqp::stmts(
  1508. ($!iterator := iterator),
  1509. ($!full = full),
  1510. ($!lastn := nqp::setelems(nqp::list, $!size = size)),
  1511. nqp::setelems($!lastn, 0),
  1512. self
  1513. )
  1514. }
  1515. method new(\iterator,\n,\action,\f) {
  1516. nqp::if(
  1517. iterator.is-lazy,
  1518. X::Cannot::Lazy.new(:action(action)).throw,
  1519. nqp::if(
  1520. nqp::istype(n,Whatever),
  1521. iterator, # * just give back itself
  1522. nqp::if(
  1523. n <= 0, # must be HLL comparison
  1524. Rakudo::Iterator.Empty, # negative is just nothing
  1525. nqp::if(
  1526. (nqp::istype(n,Int)
  1527. && nqp::isbig_I(nqp::decont(n)))
  1528. || n == Inf,
  1529. iterator, # big value = itself
  1530. nqp::create(self)!SET-SELF(iterator,n,f)
  1531. )
  1532. )
  1533. )
  1534. )
  1535. }
  1536. method !next() is raw {
  1537. nqp::stmts(
  1538. (my int $index = $!index),
  1539. ($!index = nqp::mod_i(nqp::add_i($!index,1),$!size)),
  1540. ($!todo = nqp::sub_i($!todo,1)),
  1541. nqp::atpos($!lastn,$index)
  1542. )
  1543. }
  1544. method pull-one() is raw {
  1545. nqp::if(
  1546. $!todo,
  1547. self!next,
  1548. nqp::if(
  1549. nqp::defined($!iterator),
  1550. nqp::stmts(
  1551. (my int $index),
  1552. (my int $size = $!size),
  1553. nqp::until(
  1554. nqp::eqaddr(
  1555. (my $pulled := $!iterator.pull-one),IterationEnd),
  1556. nqp::stmts(
  1557. nqp::bindpos($!lastn, $index, $pulled),
  1558. ($index = nqp::mod_i(nqp::add_i($index,1),$size))
  1559. )
  1560. ),
  1561. nqp::if(
  1562. nqp::iseq_i(nqp::elems($!lastn),$size), # full set
  1563. nqp::stmts(
  1564. ($!index = $index),
  1565. ($!todo = $!size)
  1566. ),
  1567. ($!todo = # not a full set, $!index still at 0
  1568. nqp::if($!full,0,nqp::elems($!lastn))),
  1569. ),
  1570. ($!iterator := Mu), # done iterating
  1571. nqp::if($!todo, self!next, IterationEnd)
  1572. ),
  1573. IterationEnd
  1574. )
  1575. )
  1576. }
  1577. }.new(iterator, n, action, $full)
  1578. }
  1579. # Return the last value of the given source iterator (if any).
  1580. # Also needs the action string to be used in X::Cannot::Lazy if
  1581. # the source iterator turns out to be lazy.
  1582. method LastValue(\iterator, $action) is raw {
  1583. nqp::if(
  1584. iterator.is-lazy,
  1585. X::Cannot::Lazy.new(:$action).throw,
  1586. nqp::stmts(
  1587. (my $result := IterationEnd),
  1588. nqp::until(
  1589. nqp::eqaddr((my $pulled := iterator.pull-one),IterationEnd),
  1590. ($result := $pulled)
  1591. ),
  1592. $result
  1593. )
  1594. )
  1595. }
  1596. # Return an iterator given a List and an iterator that generates
  1597. # an IterationBuffer of indexes for each pull. Each value is
  1598. # is a List with the mapped elements.
  1599. method ListIndexes(\list,\indexes) {
  1600. nqp::if(
  1601. (my int $elems = list.elems), # reifies
  1602. class :: does Iterator { # actually need to do some mapping
  1603. has $!list;
  1604. has $!indexes;
  1605. method !SET-SELF(\list,\indexes) {
  1606. nqp::stmts(
  1607. ($!list := nqp::getattr(list,List,'$!reified')),
  1608. ($!indexes := indexes),
  1609. self
  1610. )
  1611. }
  1612. method new(\l,\i) { nqp::create(self)!SET-SELF(l,i) }
  1613. method pull-one() {
  1614. nqp::if(
  1615. nqp::eqaddr(
  1616. (my $buffer := $!indexes.pull-one),
  1617. IterationEnd
  1618. ),
  1619. IterationEnd,
  1620. nqp::stmts(
  1621. (my int $elems = nqp::elems($buffer)),
  1622. (my int $i = -1),
  1623. nqp::while( # repurpose buffer for result
  1624. nqp::islt_i(($i = nqp::add_i($i,1)),$elems),
  1625. nqp::bindpos($buffer,$i,
  1626. nqp::atpos($!list,nqp::atpos($buffer,$i))
  1627. )
  1628. ),
  1629. nqp::p6bindattrinvres(
  1630. nqp::create(List),List,'$!reified',$buffer)
  1631. )
  1632. )
  1633. }
  1634. }.new(list,indexes),
  1635. Rakudo::Iterator.OneValue(nqp::create(List)) # only one
  1636. )
  1637. }
  1638. # Returns an iterator that handles all properties of a bare -loop-
  1639. # Takes a Callable to be considered the body of the loop.
  1640. method Loop(&body) {
  1641. class :: does SlippyIterator {
  1642. has &!body;
  1643. method new(&body) {
  1644. nqp::p6bindattrinvres(nqp::create(self),self,'&!body',&body)
  1645. }
  1646. method pull-one() {
  1647. my $result;
  1648. my int $stopped;
  1649. nqp::if(
  1650. $!slipping && nqp::not_i(
  1651. nqp::eqaddr(($result := self.slip-one),IterationEnd)
  1652. ),
  1653. $result,
  1654. nqp::stmts(
  1655. nqp::until(
  1656. $stopped,
  1657. nqp::stmts(
  1658. ($stopped = 1),
  1659. nqp::handle(
  1660. nqp::if(
  1661. nqp::istype(($result := &!body()),Slip),
  1662. ($stopped = nqp::eqaddr(
  1663. ($result := self.start-slip($result)),
  1664. IterationEnd
  1665. ))
  1666. ),
  1667. 'NEXT', ($stopped = 0),
  1668. 'REDO', ($stopped = 0),
  1669. 'LAST', ($result := IterationEnd)
  1670. )
  1671. ),
  1672. :nohandler
  1673. ),
  1674. $result
  1675. )
  1676. )
  1677. }
  1678. method is-lazy(--> True) { }
  1679. }.new(&body)
  1680. }
  1681. # An often occurring use of the Mappy role to generate all of the
  1682. # keys of a Map / Hash. Takes a Map / Hash as the only parameter.
  1683. method Mappy-keys(\map) {
  1684. class :: does Rakudo::Iterator::Mappy {
  1685. method pull-one() {
  1686. nqp::if(
  1687. $!iter,
  1688. nqp::iterkey_s(nqp::shift($!iter)),
  1689. IterationEnd
  1690. )
  1691. }
  1692. method push-all($target --> IterationEnd) {
  1693. nqp::while(
  1694. $!iter,
  1695. $target.push(nqp::iterkey_s(nqp::shift($!iter)))
  1696. )
  1697. }
  1698. }.new(map)
  1699. }
  1700. # An often occurring use of the Mappy role to generate alternating
  1701. # key and values of a Map/Hash in which each value is a Pair to
  1702. # be interpreted as the actual key/value. Takes a Map / Hash as
  1703. # the only parameter.
  1704. method Mappy-kv-from-pairs(\map) {
  1705. class :: does Mappy {
  1706. has Mu $!value;
  1707. method pull-one() is raw {
  1708. nqp::if(
  1709. $!value.DEFINITE,
  1710. nqp::stmts(
  1711. (my $tmp := $!value),
  1712. ($!value := nqp::null),
  1713. $tmp
  1714. ),
  1715. nqp::if(
  1716. $!iter,
  1717. nqp::stmts(
  1718. ($tmp := nqp::decont(nqp::iterval(nqp::shift($!iter)))),
  1719. ($!value := nqp::getattr($tmp,Pair,'$!value')),
  1720. (nqp::getattr($tmp,Pair,'$!key'))
  1721. ),
  1722. IterationEnd
  1723. )
  1724. )
  1725. }
  1726. method skip-one() { # must define our own skip-one
  1727. nqp::if(
  1728. $!value.DEFINITE,
  1729. nqp::stmts(
  1730. ($!value := nqp::null),
  1731. 1
  1732. ),
  1733. nqp::if(
  1734. $!iter,
  1735. nqp::stmts(
  1736. ($!value := nqp::getattr(
  1737. nqp::decont(nqp::iterval(nqp::shift($!iter))),
  1738. Pair,
  1739. '$!value'
  1740. )),
  1741. 1
  1742. )
  1743. )
  1744. )
  1745. }
  1746. method push-all($target --> IterationEnd) {
  1747. nqp::while(
  1748. $!iter,
  1749. nqp::stmts( # doesn't sink
  1750. (my $tmp := nqp::decont(nqp::iterval(nqp::shift($!iter)))),
  1751. ($target.push(nqp::getattr($tmp,Pair,'$!key'))),
  1752. ($target.push(nqp::getattr($tmp,Pair,'$!value')))
  1753. )
  1754. )
  1755. }
  1756. method count-only() {
  1757. nqp::p6box_i(
  1758. nqp::add_i(nqp::elems($!storage),nqp::elems($!storage))
  1759. )
  1760. }
  1761. }.new(map)
  1762. }
  1763. # An often occurring use of the Mappy role to generate all of the
  1764. # values of a Map / Hash. Takes a Map / Hash as the only parameter.
  1765. method Mappy-values(\map) {
  1766. class :: does Mappy {
  1767. method pull-one() is raw {
  1768. nqp::if(
  1769. $!iter,
  1770. nqp::iterval(nqp::shift($!iter)),
  1771. IterationEnd
  1772. )
  1773. }
  1774. method push-all($target --> IterationEnd) {
  1775. nqp::while( # doesn't sink
  1776. $!iter,
  1777. $target.push(nqp::iterval(nqp::shift($!iter)))
  1778. )
  1779. }
  1780. }.new(map)
  1781. }
  1782. # Return an iterator that will iterate over a source iterator and an
  1783. # iterator generating monotonically increasing index values from a
  1784. # given offset. Optionally, call block if an out-of-sequence index
  1785. # value is obtained, or simply ignore out of sequence index values.
  1786. method MonotonicIndexes(\source,\indexes,\offset,&out?) {
  1787. class :: does Iterator {
  1788. has $!source; # source iterator
  1789. has $!indexes; # iterator providing index values
  1790. has int $!next; # virtual index of next source value
  1791. has &!out; # callable for out of sequence values
  1792. method !SET-SELF($!source,$!indexes,\offset,&!out) {
  1793. $!next = offset;
  1794. self
  1795. }
  1796. method new(\s,\i,\o,\out) { nqp::create(self)!SET-SELF(s,i,o,out) }
  1797. method pull-one() is raw {
  1798. nqp::stmts(
  1799. nqp::until(
  1800. nqp::eqaddr(
  1801. (my $got := $!indexes.pull-one),
  1802. IterationEnd
  1803. ),
  1804. nqp::if(
  1805. nqp::istype((my $number = +$got),Failure),
  1806. $number.throw,
  1807. nqp::if(
  1808. nqp::isle_i($!next,(my int $index = $number.Int)),
  1809. nqp::stmts( # possibly valid index
  1810. nqp::while(
  1811. nqp::islt_i($!next,$index) && $!source.skip-one,
  1812. ($!next = nqp::add_i($!next,1))
  1813. ),
  1814. (return nqp::if(
  1815. nqp::iseq_i($!next,$index),
  1816. nqp::stmts(
  1817. ($!next = nqp::add_i($!next,1)),
  1818. $!source.pull-one
  1819. ),
  1820. IterationEnd
  1821. ))
  1822. ),
  1823. nqp::if(&out,out($index,$!next)) # out of sequence
  1824. )
  1825. )
  1826. ),
  1827. IterationEnd
  1828. )
  1829. }
  1830. }.new(source,indexes,offset,&out)
  1831. }
  1832. # Returns an iterator for the next N values of given iterator.
  1833. method NextNValues(\iterator,\times) {
  1834. class :: does Iterator {
  1835. has $!iterator;
  1836. has int $!times;
  1837. method !SET-SELF($!iterator,$!times) { self }
  1838. method new(\iterator,\times) {
  1839. nqp::if(
  1840. nqp::istype(times,Whatever),
  1841. iterator, # * just give back itself
  1842. nqp::if(
  1843. times <= 0, # must be HLL comparison
  1844. Rakudo::Iterator.Empty, # negative is just nothing
  1845. nqp::if(
  1846. (nqp::istype(times,Int)
  1847. && nqp::isbig_I(nqp::decont(times)))
  1848. || times == Inf,
  1849. iterator, # big value = itself
  1850. nqp::create(self)!SET-SELF(iterator,times)
  1851. )
  1852. )
  1853. )
  1854. }
  1855. method pull-one() is raw {
  1856. nqp::if(
  1857. nqp::isgt_i($!times,0),
  1858. nqp::if(
  1859. nqp::eqaddr(
  1860. (my $pulled := $!iterator.pull-one),
  1861. IterationEnd
  1862. ),
  1863. nqp::stmts(
  1864. ($!times = 0),
  1865. IterationEnd
  1866. ),
  1867. nqp::stmts(
  1868. ($!times = nqp::sub_i($!times,1)),
  1869. $pulled
  1870. )
  1871. ),
  1872. IterationEnd
  1873. )
  1874. }
  1875. }.new(iterator,times)
  1876. }
  1877. # Return an iterator that only will return the given value once.
  1878. # Basically the same as 42 xx 1.
  1879. method OneValue(\value) {
  1880. class :: does Iterator {
  1881. has Mu $!value;
  1882. method new(\value) {
  1883. nqp::p6bindattrinvres(nqp::create(self),self,'$!value',value)
  1884. }
  1885. method pull-one() is raw {
  1886. nqp::if(
  1887. nqp::isnull($!value),
  1888. IterationEnd,
  1889. nqp::stmts(
  1890. (my Mu $value := $!value),
  1891. ($!value := nqp::null),
  1892. $value
  1893. )
  1894. )
  1895. }
  1896. method push-all($target --> IterationEnd) {
  1897. nqp::stmts(
  1898. nqp::unless(nqp::isnull($!value),$target.push($!value)),
  1899. ($!value := nqp::null)
  1900. )
  1901. }
  1902. method sink-all(--> IterationEnd) { $!value := nqp::null }
  1903. method count-only(--> 1) { }
  1904. method bool-only(--> True) { }
  1905. }.new(value)
  1906. }
  1907. # Return an iterator that only will return the given value for the
  1908. # given number of times. Basically the same as 42 xx N.
  1909. method OneValueTimes(Mu \value,\times) {
  1910. class :: does Iterator {
  1911. has Mu $!value;
  1912. has Int $!times;
  1913. has int $!is-lazy;
  1914. method !SET-SELF(Mu \value,\times) {
  1915. nqp::stmts(
  1916. ($!value := value),
  1917. ($!times = times),
  1918. ($!is-lazy = nqp::isbig_I(nqp::decont(times))),
  1919. self
  1920. )
  1921. }
  1922. method new(Mu \value,\times) {
  1923. nqp::if(
  1924. times > 0,
  1925. nqp::create(self)!SET-SELF(value,times),
  1926. Rakudo::Iterator.Empty
  1927. )
  1928. }
  1929. method pull-one() is raw {
  1930. nqp::if(
  1931. $!times,
  1932. nqp::stmts(
  1933. --$!times,
  1934. $!value
  1935. ),
  1936. IterationEnd
  1937. )
  1938. }
  1939. method push-all($target --> IterationEnd) {
  1940. nqp::while(
  1941. $!times,
  1942. nqp::stmts(
  1943. --$!times,
  1944. $target.push($!value)
  1945. )
  1946. )
  1947. }
  1948. method skip-one() { nqp::if($!times,$!times--) }
  1949. method is-lazy() { nqp::p6bool($!is-lazy) }
  1950. method sink-all(--> IterationEnd) { $!times = 0 }
  1951. method count-only() { $!times }
  1952. method bool-only(--> True) { }
  1953. }.new(value,times)
  1954. }
  1955. # Return an iterator that will generate a pair with the index as the
  1956. # key and as value the value of the given iterator, basically the
  1957. # .pairs functionality on 1 dimensional lists.
  1958. method Pair(\iterator) {
  1959. class :: does Iterator {
  1960. has Mu $!iter;
  1961. has int $!key;
  1962. method !SET-SELF(\iter) { $!iter := iter; $!key = -1; self }
  1963. method new(\iter) { nqp::create(self)!SET-SELF(iter) }
  1964. method pull-one() is raw {
  1965. nqp::if(
  1966. nqp::eqaddr((my $pulled := $!iter.pull-one),IterationEnd),
  1967. IterationEnd,
  1968. Pair.new(($!key = nqp::add_i($!key,1)),$pulled)
  1969. )
  1970. }
  1971. method push-all($target --> IterationEnd) {
  1972. my $pulled;
  1973. my int $key = -1;
  1974. nqp::until(
  1975. nqp::eqaddr(($pulled := $!iter.pull-one),IterationEnd),
  1976. $target.push(Pair.new(($key = nqp::add_i($key,1)),$pulled))
  1977. )
  1978. }
  1979. }.new(iterator)
  1980. }
  1981. # Return an iterator for a given number of permutations. Also specify
  1982. # whether an IterationBuffer should be returned for each iteration (1),
  1983. # or a List (0). Basically the workhorse of permutations.
  1984. method Permutations($n, int $b) {
  1985. nqp::if(
  1986. $n > nqp::if(nqp::iseq_i($?BITS,32),13,20), # must be HLL comparison
  1987. (die "Cowardly refusing to permutate more than {
  1988. $?BITS == 32 ?? 13 !! 20
  1989. } elements, tried $n"),
  1990. nqp::if(
  1991. $n < 1, # must be HLL comparison
  1992. Rakudo::Iterator.OneValue(
  1993. nqp::create(nqp::if($b,IterationBuffer,List))
  1994. ),
  1995. # See: L<https://en.wikipedia.org/wiki/Permutation#Generation_in_lexicographic_order>
  1996. class :: does Iterator {
  1997. has int $!n;
  1998. has int $!b;
  1999. has int $!todo;
  2000. has int $!elems;
  2001. has $!next;
  2002. method !SET-SELF(int $n, int $b) {
  2003. nqp::stmts(
  2004. ($!n = $n),
  2005. ($!b = $b),
  2006. ($!todo = 1),
  2007. (my int $i = 1),
  2008. nqp::while(
  2009. nqp::isle_i(($i = nqp::add_i($i,1)),$n),
  2010. ($!todo = nqp::mul_i($!todo,$i))
  2011. ),
  2012. ($!elems = $!todo),
  2013. ($!next :=
  2014. nqp::setelems(nqp::create(IterationBuffer),$n)),
  2015. ($i = -1),
  2016. nqp::while(
  2017. nqp::islt_i(($i = nqp::add_i($i,1)),$n),
  2018. nqp::bindpos($!next,$i,nqp::clone($i))
  2019. ),
  2020. self
  2021. )
  2022. }
  2023. method new(\n,\b) { nqp::create(self)!SET-SELF(n,b) }
  2024. method pull-one {
  2025. nqp::if(
  2026. nqp::isge_i(($!todo = nqp::sub_i($!todo,1)),0),
  2027. nqp::stmts(
  2028. (my $permuted := nqp::clone($!next)),
  2029. nqp::if(
  2030. $!todo, # need to calculate next one
  2031. nqp::stmts( # largest index k such that a[k] < a[k+1]
  2032. (my int $k = nqp::sub_i($!n,2)),
  2033. nqp::until(
  2034. nqp::islt_i(
  2035. nqp::atpos($!next,$k),
  2036. nqp::atpos($!next,nqp::add_i($k,1))
  2037. ),
  2038. ($k = nqp::sub_i($k,1)),
  2039. ),
  2040. (my int $l = nqp::sub_i($!n,1)),
  2041. nqp::until(
  2042. nqp::islt_i( # largest index l>k where a[k] < a[l]
  2043. nqp::atpos($!next,$k),
  2044. nqp::atpos($!next,$l)
  2045. ),
  2046. ($l = nqp::sub_i($l,1))
  2047. ),
  2048. (my $tmp := nqp::atpos($!next,$k)),
  2049. nqp::bindpos($!next,$k,nqp::atpos($!next,$l)),
  2050. nqp::bindpos($!next,$l,$tmp)
  2051. )
  2052. ),
  2053. ($l = $!n),
  2054. nqp::until(
  2055. nqp::isge_i(
  2056. ($k = nqp::add_i($k,1)),
  2057. ($l = nqp::sub_i($l,1))
  2058. ),
  2059. nqp::stmts(
  2060. ($tmp := nqp::atpos($!next,$k)),
  2061. nqp::bindpos($!next,$k,nqp::atpos($!next,$l)),
  2062. nqp::bindpos($!next,$l,$tmp)
  2063. )
  2064. ),
  2065. nqp::if(
  2066. $b,
  2067. $permuted,
  2068. nqp::p6bindattrinvres(
  2069. nqp::create(List),List,'$!reified',$permuted)
  2070. )
  2071. ),
  2072. IterationEnd
  2073. )
  2074. }
  2075. method count-only { $!elems }
  2076. method bool-only(--> True) { }
  2077. }.new($n,$b)
  2078. )
  2079. )
  2080. }
  2081. # Return an iterator for an Array that has been completely reified
  2082. # already. Returns a assignable container for elements don't exist
  2083. # before the end of the reified array.
  2084. method ReifiedArray(\array) {
  2085. class :: does Iterator {
  2086. has $!reified;
  2087. has $!descriptor;
  2088. has int $!i;
  2089. method !SET-SELF(\array) {
  2090. nqp::stmts(
  2091. ($!reified := nqp::getattr(array, List, '$!reified')),
  2092. ($!descriptor := nqp::getattr(array, Array, '$!descriptor')),
  2093. ($!i = -1),
  2094. self
  2095. )
  2096. }
  2097. method new(\array) { nqp::create(self)!SET-SELF(array) }
  2098. method pull-one() is raw {
  2099. nqp::ifnull(
  2100. nqp::atpos($!reified,$!i = nqp::add_i($!i,1)),
  2101. nqp::if(
  2102. nqp::islt_i($!i,nqp::elems($!reified)), # found a hole
  2103. nqp::p6bindattrinvres(
  2104. (my \v := nqp::p6scalarfromdesc($!descriptor)),
  2105. Scalar,
  2106. '$!whence',
  2107. -> { nqp::bindpos($!reified,$!i,v) }
  2108. ),
  2109. IterationEnd
  2110. )
  2111. )
  2112. }
  2113. method push-all($target --> IterationEnd) {
  2114. nqp::stmts(
  2115. (my int $elems = nqp::elems($!reified)),
  2116. nqp::while( # doesn't sink
  2117. nqp::islt_i($!i = nqp::add_i($!i,1),$elems),
  2118. $target.push(nqp::ifnull(
  2119. nqp::atpos($!reified,$!i),
  2120. nqp::p6bindattrinvres(
  2121. (my \v := nqp::p6scalarfromdesc($!descriptor)),
  2122. Scalar,
  2123. '$!whence',
  2124. -> { nqp::bindpos($!reified,$!i,v) }
  2125. )
  2126. ))
  2127. )
  2128. )
  2129. }
  2130. method skip-one() {
  2131. nqp::islt_i(
  2132. ($!i = nqp::add_i($!i,1)),
  2133. nqp::elems($!reified)
  2134. )
  2135. }
  2136. method skip-at-least(int $toskip) {
  2137. nqp::islt_i(
  2138. ($!i =
  2139. nqp::add_i($!i,nqp::if(nqp::isgt_i($toskip,0),$toskip,0))),
  2140. nqp::elems($!reified)
  2141. )
  2142. }
  2143. method count-only() { nqp::p6box_i(nqp::elems($!reified)) }
  2144. method bool-only() { nqp::p6bool(nqp::elems($!reified)) }
  2145. method sink-all(--> IterationEnd) { $!i = nqp::elems($!reified) }
  2146. }.new(array)
  2147. }
  2148. # Return an iterator for a List that has been completely reified
  2149. # already. Returns an nqp::null for elements don't exist before
  2150. # the end of the reified list.
  2151. method ReifiedList(\list) {
  2152. class :: does Iterator {
  2153. has $!reified;
  2154. has int $!i;
  2155. method !SET-SELF(\list) {
  2156. nqp::stmts(
  2157. ($!reified := nqp::if(
  2158. nqp::istype(list,List),
  2159. nqp::getattr(list,List,'$!reified'),
  2160. list)),
  2161. ($!i = -1),
  2162. self
  2163. )
  2164. }
  2165. method new(\list) { nqp::create(self)!SET-SELF(list) }
  2166. method pull-one() is raw {
  2167. nqp::ifnull(
  2168. nqp::atpos($!reified,$!i = nqp::add_i($!i,1)),
  2169. nqp::if(
  2170. nqp::islt_i($!i,nqp::elems($!reified)), # found a hole
  2171. nqp::null, # it's a hole
  2172. IterationEnd # it's the end
  2173. )
  2174. )
  2175. }
  2176. method push-all($target --> IterationEnd) {
  2177. nqp::stmts(
  2178. (my int $elems = nqp::elems($!reified)),
  2179. (my int $i = $!i), # lexicals are faster than attributes
  2180. nqp::while( # doesn't sink
  2181. nqp::islt_i(($i = nqp::add_i($i,1)),$elems),
  2182. $target.push(nqp::atpos($!reified,$i))
  2183. ),
  2184. ($!i = $i)
  2185. )
  2186. }
  2187. method skip-one() {
  2188. nqp::islt_i(
  2189. ($!i = nqp::add_i($!i,1)),
  2190. nqp::elems($!reified)
  2191. )
  2192. }
  2193. method skip-at-least(int $toskip) {
  2194. nqp::islt_i(
  2195. ($!i =
  2196. nqp::add_i($!i,nqp::if(nqp::isgt_i($toskip,0),$toskip,0))),
  2197. nqp::elems($!reified)
  2198. )
  2199. }
  2200. method count-only() { nqp::p6box_i(nqp::elems($!reified)) }
  2201. method bool-only() { nqp::p6bool(nqp::elems($!reified)) }
  2202. method sink-all(--> IterationEnd) { $!i = nqp::elems($!reified) }
  2203. }.new(list)
  2204. }
  2205. # Return an iterator that produces values in reverse order for a
  2206. # List that has been completely reified already. Returns an nqp::null
  2207. # for elements don't exist before the end of the reified list.
  2208. method ReifiedListReverse(\list) {
  2209. class :: does Iterator {
  2210. has $!reified;
  2211. has int $!i;
  2212. method !SET-SELF(\list) {
  2213. nqp::stmts(
  2214. ($!reified := nqp::if(
  2215. nqp::istype(list,List),
  2216. nqp::getattr(list,List,'$!reified'),
  2217. list)),
  2218. ($!i = nqp::elems($!reified)),
  2219. self
  2220. )
  2221. }
  2222. method new(\list) { nqp::create(self)!SET-SELF(list) }
  2223. method pull-one() is raw {
  2224. nqp::if(
  2225. $!i,
  2226. nqp::atpos($!reified,$!i = nqp::sub_i($!i,1)),
  2227. IterationEnd
  2228. )
  2229. }
  2230. method push-all($target --> IterationEnd) {
  2231. nqp::stmts(
  2232. (my int $i = nqp::elems($!reified)),
  2233. nqp::while( # doesn't sink
  2234. $i,
  2235. $target.push(nqp::atpos($!reified,($i = nqp::sub_i($i,1))))
  2236. ),
  2237. ($!i = 0)
  2238. )
  2239. }
  2240. method skip-one() {
  2241. nqp::if(
  2242. $!i,
  2243. nqp::isge_i(($!i = nqp::sub_i($!i,1)),0)
  2244. )
  2245. }
  2246. method skip-at-least(int $toskip) {
  2247. nqp::unless(
  2248. nqp::isge_i(($!i = nqp::sub_i($!i,$toskip)),0),
  2249. ($!i = 0)
  2250. )
  2251. }
  2252. method count-only() { nqp::p6box_i(nqp::elems($!reified)) }
  2253. method bool-only() { nqp::p6bool(nqp::elems($!reified)) }
  2254. method sink-all(--> IterationEnd) { $!i = 0 }
  2255. }.new(list)
  2256. }
  2257. # Return a lazy iterator that will repeat the values of a given
  2258. # source iterator indefinitely. Even when given a lazy iterator,
  2259. # it will cache the values seen to handle case that the iterator
  2260. # will exhaust after all. Only if the source iterator did not
  2261. # produce any values at all, then the returned iterator will not
  2262. # produce any either.
  2263. method Repeat(\iterator) {
  2264. class :: does Iterator {
  2265. has $!iterator;
  2266. has $!reified;
  2267. has int $!i;
  2268. method !SET-SELF(\iterator) {
  2269. nqp::stmts(
  2270. ($!iterator := iterator),
  2271. ($!reified := nqp::create(IterationBuffer)),
  2272. self
  2273. )
  2274. }
  2275. method new(\iter) { nqp::create(self)!SET-SELF(iter) }
  2276. method pull-one() is raw {
  2277. nqp::if(
  2278. nqp::isnull($!iterator),
  2279. nqp::atpos( # supplying from cache
  2280. $!reified,
  2281. nqp::mod_i(
  2282. ($!i = nqp::add_i($!i,1)),
  2283. nqp::elems($!reified)
  2284. )
  2285. ),
  2286. nqp::if( # supplying from iterator
  2287. nqp::eqaddr(
  2288. (my $pulled := $!iterator.pull-one),
  2289. IterationEnd
  2290. ),
  2291. nqp::if(
  2292. nqp::elems($!reified),
  2293. nqp::stmts( # exhausted, something in cache
  2294. ($!iterator := nqp::null),
  2295. nqp::atpos($!reified,0)
  2296. ),
  2297. IterationEnd # exhausted, nothing in cache
  2298. ),
  2299. nqp::push( # cache and supply
  2300. $!reified,
  2301. $pulled
  2302. )
  2303. )
  2304. )
  2305. }
  2306. method is-lazy(--> True) { } # we're lazy, always
  2307. }.new(iterator)
  2308. }
  2309. # Returns an iterator that handles all properties of a -repeat- with
  2310. # a condition. Takes a Callable to be considered the body of the loop,
  2311. # and a Callable for the condition..
  2312. method RepeatLoop(&body, &cond) {
  2313. class :: does SlippyIterator {
  2314. has $!body;
  2315. has $!cond;
  2316. has int $!skip;
  2317. method !SET-SELF(\body,\cond) {
  2318. nqp::stmts(
  2319. ($!body := body),
  2320. ($!cond := cond),
  2321. ($!skip = 1),
  2322. self
  2323. )
  2324. }
  2325. method new(\body,\cond) {
  2326. nqp::create(self)!SET-SELF(body,cond)
  2327. }
  2328. method pull-one() {
  2329. if $!slipping && nqp::not_i(
  2330. nqp::eqaddr((my $result := self.slip-one),IterationEnd)
  2331. ) {
  2332. $result
  2333. }
  2334. else {
  2335. nqp::if(
  2336. $!skip || $!cond(),
  2337. nqp::stmts(
  2338. ($!skip = 0),
  2339. nqp::until(
  2340. (my int $stopped),
  2341. nqp::stmts(
  2342. ($stopped = 1),
  2343. nqp::handle(
  2344. nqp::if(
  2345. nqp::istype(($result := $!body()),Slip),
  2346. ($stopped = nqp::eqaddr(
  2347. ($result := self.start-slip($result)),
  2348. IterationEnd
  2349. ) && nqp::if($!cond(),0,1))
  2350. ),
  2351. 'NEXT', ($stopped = nqp::if($!cond(),0,1)),
  2352. 'REDO', ($stopped = 0),
  2353. 'LAST', ($result := IterationEnd)
  2354. )
  2355. ),
  2356. :nohandler
  2357. ),
  2358. $result
  2359. ),
  2360. IterationEnd
  2361. )
  2362. }
  2363. }
  2364. }.new(&body,&cond)
  2365. }
  2366. # Return a lazy iterator that keeps calling .roll on the given object.
  2367. # Basically the functionality of List.roll(*), but could be on any
  2368. # object that has a .roll method.
  2369. method Roller(\source) {
  2370. Rakudo::Iterator.Callable( { source.roll }, True )
  2371. }
  2372. # Return an iterator that rotorizes the given iterator with the
  2373. # given cycle. If the cycle is a Cool, then it is assumed to
  2374. # be a single Int value to R:It.Batch with. Otherwise it is
  2375. # considered to be something Iterable that will be repeated
  2376. # until the source iterator is exhausted. The third parameter
  2377. # indicates whether a partial result is acceptable when the
  2378. # source iterator is exhausted.
  2379. method Rotor(\iterator,\cycle,\partial) {
  2380. class :: does Iterator {
  2381. has $!iterator;
  2382. has $!cycle;
  2383. has $!buffer;
  2384. has int $!complete;
  2385. method !SET-SELF(\iterator,\cycle,\partial) {
  2386. nqp::stmts(
  2387. ($!iterator := iterator),
  2388. ($!cycle := Rakudo::Iterator.Repeat(cycle.iterator)),
  2389. ($!buffer := nqp::create(IterationBuffer)),
  2390. ($!complete = !partial),
  2391. self
  2392. )
  2393. }
  2394. method new(\iterator,\cycle,\partial) {
  2395. nqp::if(
  2396. nqp::istype(cycle,Iterable),
  2397. nqp::create(self)!SET-SELF(iterator,cycle,partial),
  2398. Rakudo::Iterator.Batch(iterator,cycle,partial)
  2399. )
  2400. }
  2401. method pull-one() is raw {
  2402. nqp::stmts(
  2403. nqp::if(
  2404. nqp::istype((my $todo := $!cycle.pull-one),Pair),
  2405. nqp::stmts(
  2406. (my $size := $todo.key),
  2407. nqp::if(
  2408. nqp::istype($size,Whatever),
  2409. nqp::stmts( # eat everything
  2410. (my int $elems = -1),
  2411. ($!complete = 0)
  2412. ),
  2413. nqp::if(
  2414. $size < 1, # must be HLL comparison
  2415. X::OutOfRange.new(
  2416. what => "Rotorizing sublist length is",
  2417. got => $size,
  2418. range => "1..^Inf",
  2419. ).throw,
  2420. nqp::if(
  2421. $size == Inf || (
  2422. nqp::istype($size,Int)
  2423. && nqp::isbig_I(nqp::decont($size))
  2424. ),
  2425. nqp::stmts( # eat everything
  2426. ($elems = -1),
  2427. ($!complete = 0)
  2428. ),
  2429. nqp::if(
  2430. nqp::isle_i(
  2431. nqp::add_i(
  2432. ($elems = $size.Int),
  2433. (my int $gap = $todo.value.Int)
  2434. ),
  2435. -1
  2436. ),
  2437. X::OutOfRange.new( # gap out of range
  2438. what => "Rotorizing gap is",
  2439. got => $gap,
  2440. range => "-$elems..^Inf",
  2441. comment => "\nEnsure a negative gap is not larger than the length of the sublist",
  2442. ).throw
  2443. )
  2444. )
  2445. )
  2446. )
  2447. ),
  2448. nqp::if( # just a size
  2449. nqp::istype($todo,Whatever),
  2450. nqp::stmts( # eat everything
  2451. ($elems = -1),
  2452. ($!complete = 0)
  2453. ),
  2454. nqp::if(
  2455. $todo < 1, # must be HLL comparison
  2456. X::OutOfRange.new( # size out of range
  2457. what => "Rotorizing sublist length is",
  2458. got => $todo,
  2459. range => "1..^Inf",
  2460. comment => "\nDid you mean to specify a Pair with => $todo?"
  2461. ).throw,
  2462. nqp::if(
  2463. (nqp::istype($todo,Int)
  2464. && nqp::isbig_I(nqp::decont($todo)))
  2465. || $todo == Inf,
  2466. nqp::stmts( # eat everything
  2467. ($elems = -1),
  2468. ($!complete = 0)
  2469. ),
  2470. ($elems = $todo.Int)
  2471. )
  2472. )
  2473. )
  2474. ),
  2475. nqp::until( # fill the buffer
  2476. nqp::isge_i(nqp::elems($!buffer),$elems)
  2477. || nqp::eqaddr(
  2478. (my $pulled := $!iterator.pull-one),
  2479. IterationEnd
  2480. ),
  2481. nqp::push($!buffer,$pulled)
  2482. ),
  2483. nqp::if(
  2484. nqp::not_i(nqp::elems($!buffer))
  2485. || (nqp::eqaddr($pulled,IterationEnd)
  2486. && $!complete
  2487. && nqp::islt_i(nqp::elems($!buffer),$elems)
  2488. ),
  2489. IterationEnd, # done
  2490. nqp::if(
  2491. nqp::islt_i($gap,0),
  2492. nqp::stmts( # keep some for next
  2493. (my $result := nqp::p6bindattrinvres(
  2494. nqp::create(List),List,'$!reified',
  2495. nqp::clone($!buffer)
  2496. )),
  2497. nqp::if(
  2498. nqp::islt_i(nqp::elems($!buffer),$elems),
  2499. nqp::setelems($!buffer,0), # was :partial, now done
  2500. nqp::splice($!buffer,$empty,0,nqp::add_i($elems,$gap))
  2501. ),
  2502. $result
  2503. ),
  2504. nqp::stmts(
  2505. nqp::if(
  2506. nqp::isgt_i($gap,0),
  2507. $!iterator.skip-at-least($gap) # need to skip a few
  2508. ),
  2509. nqp::if(
  2510. nqp::isle_i(nqp::elems($!buffer),$elems),
  2511. nqp::stmts( # whole buffer ok
  2512. ($result := nqp::p6bindattrinvres(
  2513. nqp::create(List),List,'$!reified',
  2514. $!buffer
  2515. )),
  2516. ($!buffer := nqp::create(IterationBuffer))
  2517. ),
  2518. nqp::stmts( # partial buffer ok
  2519. ($result := nqp::p6bindattrinvres(
  2520. nqp::create(List),List,'$!reified',
  2521. nqp::splice(
  2522. nqp::clone($!buffer),
  2523. $empty,
  2524. $elems,
  2525. nqp::sub_i(nqp::elems($!buffer),$elems)
  2526. )
  2527. )),
  2528. nqp::splice($!buffer,$empty,0,$elems)
  2529. )
  2530. ),
  2531. $result
  2532. )
  2533. )
  2534. )
  2535. )
  2536. }
  2537. method is-lazy() { $!iterator.is-lazy }
  2538. }.new(iterator,cycle,partial)
  2539. }
  2540. # Return an iterator that will roundrobin the given iterables
  2541. # (with &[,]). Basically the functionality of roundrobin(@a,@b)
  2542. method RoundrobinIterables(@iterables) {
  2543. nqp::if(
  2544. nqp::isgt_i((my int $n = @iterables.elems),1), # reifies
  2545. class :: does Iterator {
  2546. has $!iters;
  2547. has int $!lazy;
  2548. method !SET-SELF(\iterables) {
  2549. nqp::stmts(
  2550. (my $iterables := nqp::getattr(iterables,List,'$!reified')),
  2551. (my int $elems = nqp::elems($iterables)),
  2552. ($!iters := nqp::setelems(nqp::list,$elems)),
  2553. (my int $i = -1),
  2554. nqp::while(
  2555. nqp::islt_i(($i = nqp::add_i($i,1)),$elems),
  2556. nqp::bindpos($!iters,$i,
  2557. nqp::if(
  2558. nqp::iscont(my $elem := nqp::atpos($iterables,$i)),
  2559. Rakudo::Iterator.OneValue($elem),
  2560. nqp::stmts(
  2561. nqp::if($elem.is-lazy,($!lazy = 1)),
  2562. $elem.iterator
  2563. )
  2564. )
  2565. )
  2566. ),
  2567. self
  2568. )
  2569. }
  2570. method new(\iterables) { nqp::create(self)!SET-SELF(iterables) }
  2571. method pull-one() {
  2572. nqp::if(
  2573. nqp::isnull($!iters),
  2574. IterationEnd,
  2575. nqp::stmts(
  2576. (my int $i = -1),
  2577. (my int $elems = nqp::elems($!iters)),
  2578. (my $buf := nqp::create(IterationBuffer)),
  2579. nqp::until(
  2580. nqp::iseq_i(($i = nqp::add_i($i,1)),$elems),
  2581. nqp::if(
  2582. nqp::eqaddr(
  2583. (my $pulled := nqp::atpos($!iters,$i).pull-one),
  2584. IterationEnd
  2585. ),
  2586. nqp::stmts( # remove exhausted iterator
  2587. nqp::splice($!iters,$empty,$i,1),
  2588. ($i = nqp::sub_i($i,1)),
  2589. ($elems = nqp::sub_i($elems,1))
  2590. ),
  2591. nqp::push($buf,$pulled)
  2592. )
  2593. ),
  2594. nqp::if(
  2595. nqp::elems($buf),
  2596. nqp::p6bindattrinvres( # at least one not exhausted
  2597. nqp::create(List),List,'$!reified',$buf),
  2598. nqp::stmts( # we're done
  2599. ($!iters := nqp::null),
  2600. IterationEnd
  2601. )
  2602. )
  2603. )
  2604. )
  2605. }
  2606. method is-lazy() { nqp::p6bool($!lazy) }
  2607. }.new(@iterables),
  2608. nqp::if(
  2609. nqp::iseq_i($n,0),
  2610. Rakudo::Iterator.Empty,
  2611. nqp::atpos(nqp::getattr(@iterables,List,'$!reified'),0).iterator
  2612. )
  2613. )
  2614. }
  2615. # Return an iterator from a source iterator that is supposed to
  2616. # generate iterators. As soon as a iterator, the next iterator
  2617. # will be fetched and iterated over until exhausted.
  2618. method SequentialIterators(\source) {
  2619. class :: does Iterator {
  2620. has $!source;
  2621. has $!current;
  2622. method !SET-SELF(\source) {
  2623. nqp::stmts(
  2624. ($!current := ($!source := source).pull-one),
  2625. self
  2626. )
  2627. }
  2628. method new(\source) { nqp::create(self)!SET-SELF(source) }
  2629. method pull-one() {
  2630. nqp::if(
  2631. nqp::eqaddr($!current,IterationEnd),
  2632. IterationEnd,
  2633. nqp::if(
  2634. nqp::eqaddr(
  2635. (my $pulled := $!current.pull-one),
  2636. IterationEnd
  2637. ),
  2638. nqp::stmts(
  2639. ($!current := $!source.pull-one),
  2640. self.pull-one
  2641. ),
  2642. $pulled
  2643. )
  2644. )
  2645. }
  2646. }.new(source)
  2647. }
  2648. # Return an iterator that generates all possible keys of the
  2649. # given shape. Each value generated is a reified List. This is
  2650. # basically a copy of the internal engine of ShapeLeaf and
  2651. # ShapeBranchi roles, but without any additional processing.
  2652. # Intended for ad-hoc iterators that feed .AT-POS on shaped lists.
  2653. method ShapeIndex(\shape) {
  2654. class :: does Iterator {
  2655. has $!dims;
  2656. has $!indices;
  2657. has int $!maxdim;
  2658. has int $!max;
  2659. method SET-SELF(\shape) {
  2660. nqp::stmts(
  2661. ($!dims := nqp::getattr(nqp::decont(shape),List,'$!reified')),
  2662. (my int $dims = nqp::elems($!dims)),
  2663. ($!indices :=
  2664. nqp::setelems(nqp::create(IterationBuffer),$dims)),
  2665. (my int $i = -1),
  2666. nqp::while(
  2667. nqp::islt_i(($i = nqp::add_i($i,1)),$dims),
  2668. nqp::bindpos($!indices,$i,0)
  2669. ),
  2670. ($!maxdim = nqp::sub_i($dims,1)),
  2671. ($!max = nqp::atpos($!dims,$!maxdim)),
  2672. self
  2673. )
  2674. }
  2675. method new(\shape) { nqp::create(self).SET-SELF(shape) }
  2676. method pull-one() is raw {
  2677. nqp::if(
  2678. $!indices,
  2679. nqp::stmts( # still iterating
  2680. (my $buf := nqp::clone($!indices)),
  2681. nqp::if(
  2682. nqp::islt_i( (my int $i =
  2683. nqp::add_i(nqp::atpos($!indices,$!maxdim),1)),
  2684. $!max
  2685. ),
  2686. nqp::bindpos($!indices,$!maxdim,$i), # ready for next
  2687. nqp::stmts( # done for now
  2688. (my int $level = $!maxdim),
  2689. nqp::until( # update indices
  2690. nqp::islt_i( # exhausted ??
  2691. ($level = nqp::sub_i($level,1)),0)
  2692. || nqp::stmts(
  2693. nqp::bindpos($!indices,nqp::add_i($level,1),0),
  2694. nqp::islt_i(
  2695. nqp::bindpos($!indices,$level,
  2696. nqp::add_i(nqp::atpos($!indices,$level),1)),
  2697. nqp::atpos($!dims,$level)
  2698. ),
  2699. ),
  2700. nqp::null
  2701. ),
  2702. nqp::if( # this was the last value
  2703. nqp::islt_i($level,0),
  2704. $!indices := nqp::null
  2705. )
  2706. )
  2707. ),
  2708. nqp::p6bindattrinvres( # what we found
  2709. nqp::create(List),List,'$!reified',$buf)
  2710. ),
  2711. IterationEnd # done iterating
  2712. )
  2713. }
  2714. }.new(shape)
  2715. }
  2716. # Return a lazy iterator that will keep producing the given value.
  2717. # Basically the functionality of 42 xx *
  2718. method UnendingValue(Mu \value) {
  2719. class :: does Iterator {
  2720. has Mu $!value;
  2721. method new(Mu \value) {
  2722. nqp::p6bindattrinvres(nqp::create(self),self,'$!value',value)
  2723. }
  2724. method pull-one() is raw { $!value }
  2725. method is-lazy(--> True) { }
  2726. }.new(value)
  2727. }
  2728. # Returns an iterator from a given iterator where the occurrence of
  2729. # a Whatever value indicates that last value seen from the source
  2730. # iterator should be repeated indefinitely until either another
  2731. # non-Whatever value is seen from the source iterator, or the source
  2732. # iterator is exhausted.
  2733. method Whatever(\source) {
  2734. class :: does Iterator {
  2735. has $!source;
  2736. has $!last;
  2737. has int $!whatever;
  2738. method new(\source) {
  2739. nqp::p6bindattrinvres(nqp::create(self),self,'$!source',source)
  2740. }
  2741. method pull-one() is raw {
  2742. nqp::if(
  2743. $!whatever,
  2744. nqp::if( # we're repeating
  2745. nqp::iseq_i($!whatever,2), # source exhausted, repeat
  2746. $!last,
  2747. nqp::if(
  2748. nqp::eqaddr(
  2749. (my $value := $!source.pull-one),
  2750. IterationEnd
  2751. ),
  2752. nqp::stmts( # exhausted now, repeat
  2753. ($!whatever = 2),
  2754. $!last
  2755. ),
  2756. nqp::if(
  2757. nqp::istype($value,Whatever),
  2758. $!last, # another Whatever, repeat
  2759. nqp::stmts( # something else, no repeat
  2760. ($!whatever = 0),
  2761. ($!last := $value)
  2762. )
  2763. )
  2764. )
  2765. ),
  2766. nqp::if( # not repeating
  2767. nqp::eqaddr(
  2768. ($value := $!source.pull-one),
  2769. IterationEnd
  2770. ),
  2771. IterationEnd, # exhausted, stop
  2772. nqp::if(
  2773. nqp::istype($value,Whatever), # start repeating
  2774. nqp::stmts(
  2775. ($!whatever = 1),
  2776. $!last
  2777. ),
  2778. ($!last := $value) # keep value for repeat
  2779. )
  2780. )
  2781. )
  2782. }
  2783. }.new(source)
  2784. }
  2785. # Returns an iterator that handles all properties of a -while- with
  2786. # a condition. Takes a Callable to be considered the body of the loop,
  2787. # and a Callable for the condition..
  2788. method WhileLoop(&body, &cond) {
  2789. class :: does SlippyIterator {
  2790. has $!body;
  2791. has $!cond;
  2792. method !SET-SELF(\body,\cond) {
  2793. nqp::stmts(
  2794. ($!body := body),
  2795. ($!cond := cond),
  2796. self
  2797. )
  2798. }
  2799. method new(\body,\cond) {
  2800. nqp::create(self)!SET-SELF(body,cond)
  2801. }
  2802. method pull-one() {
  2803. if $!slipping && nqp::not_i(
  2804. nqp::eqaddr((my $result := self.slip-one),IterationEnd)
  2805. ) {
  2806. $result
  2807. }
  2808. else {
  2809. nqp::if(
  2810. $!cond(),
  2811. nqp::stmts(
  2812. nqp::until(
  2813. (my int $stopped),
  2814. nqp::stmts(
  2815. ($stopped = 1),
  2816. nqp::handle(
  2817. nqp::if(
  2818. nqp::istype(($result := $!body()),Slip),
  2819. ($stopped = nqp::eqaddr(
  2820. ($result := self.start-slip($result)),
  2821. IterationEnd
  2822. ) && nqp::if($!cond(),0,1))
  2823. ),
  2824. 'NEXT', ($stopped = nqp::if($!cond(),0,1)),
  2825. 'REDO', ($stopped = 0),
  2826. 'LAST', ($result := IterationEnd)
  2827. )
  2828. ),
  2829. :nohandler
  2830. ),
  2831. $result
  2832. ),
  2833. IterationEnd
  2834. )
  2835. }
  2836. }
  2837. }.new(&body,&cond)
  2838. }
  2839. # Return an iterator that will zip the given iterables (with &[,])
  2840. # Basically the functionality of @a Z @b
  2841. method ZipIterables(@iterables) {
  2842. nqp::if(
  2843. nqp::isgt_i((my int $n = @iterables.elems),1), # reifies
  2844. class :: does Iterator {
  2845. has $!iters;
  2846. has int $!lazy;
  2847. method !SET-SELF(\iterables) {
  2848. nqp::stmts(
  2849. (my $iterables := nqp::getattr(iterables,List,'$!reified')),
  2850. (my int $elems = nqp::elems($iterables)),
  2851. ($!iters := nqp::setelems(nqp::list,$elems)),
  2852. ($!lazy = 1),
  2853. (my int $i = -1),
  2854. nqp::while(
  2855. nqp::islt_i(($i = nqp::add_i($i,1)),$elems),
  2856. nqp::bindpos($!iters,$i,
  2857. nqp::if(
  2858. nqp::iscont(my $elem := nqp::atpos($iterables,$i)),
  2859. nqp::stmts(
  2860. ($!lazy = 0),
  2861. Rakudo::Iterator.OneValue($elem)
  2862. ),
  2863. nqp::stmts(
  2864. nqp::unless($elem.is-lazy,($!lazy = 0)),
  2865. Rakudo::Iterator.Whatever($elem.iterator)
  2866. )
  2867. )
  2868. )
  2869. ),
  2870. self
  2871. )
  2872. }
  2873. method new(\iterables) { nqp::create(self)!SET-SELF(iterables) }
  2874. method pull-one() {
  2875. nqp::if(
  2876. nqp::isnull($!iters),
  2877. IterationEnd,
  2878. nqp::stmts(
  2879. (my int $i = -1),
  2880. (my int $elems = nqp::elems($!iters)),
  2881. (my $buf :=
  2882. nqp::setelems(nqp::create(IterationBuffer),$elems)),
  2883. nqp::until(
  2884. nqp::iseq_i(($i = nqp::add_i($i,1)),$elems)
  2885. || nqp::eqaddr(
  2886. (my $pulled := nqp::atpos($!iters,$i).pull-one),
  2887. IterationEnd
  2888. ),
  2889. nqp::bindpos($buf,$i,$pulled)
  2890. ),
  2891. nqp::if(
  2892. nqp::islt_i($i,$elems), # at least one exhausted
  2893. nqp::stmts(
  2894. ($!iters := nqp::null),
  2895. IterationEnd
  2896. ),
  2897. nqp::p6bindattrinvres(
  2898. nqp::create(List),List,'$!reified',$buf)
  2899. )
  2900. )
  2901. )
  2902. }
  2903. method is-lazy() { nqp::p6bool($!lazy) }
  2904. }.new(@iterables),
  2905. nqp::if(
  2906. nqp::iseq_i($n,0),
  2907. Rakudo::Iterator.Empty,
  2908. nqp::atpos(nqp::getattr(@iterables,List,'$!reified'),0).iterator
  2909. )
  2910. )
  2911. }
  2912. # Same as ZipIterablesOp, but takes a mapper Callable instead of
  2913. # an op. This is the underlying workhorse of ZipIterablesOp.
  2914. method ZipIterablesMap(@iterables,&mapper) {
  2915. nqp::if(
  2916. nqp::isgt_i((my int $n = @iterables.elems),1), # reifies
  2917. class :: does Iterator {
  2918. has $!iters;
  2919. has $!mapper;
  2920. has int $!lazy;
  2921. method !SET-SELF(\iterables,\mapper) {
  2922. nqp::stmts(
  2923. (my $iterables := nqp::getattr(iterables,List,'$!reified')),
  2924. (my int $elems = nqp::elems($iterables)),
  2925. ($!iters := nqp::setelems(nqp::list,$elems)),
  2926. ($!mapper := mapper),
  2927. ($!lazy = 1),
  2928. (my int $i = -1),
  2929. nqp::while(
  2930. nqp::islt_i(($i = nqp::add_i($i,1)),$elems),
  2931. nqp::bindpos($!iters,$i,
  2932. nqp::if(
  2933. nqp::iscont(my $elem := nqp::atpos($iterables,$i)),
  2934. nqp::stmts(
  2935. ($!lazy = 0),
  2936. Rakudo::Iterator.OneValue($elem)
  2937. ),
  2938. nqp::stmts(
  2939. nqp::unless($elem.is-lazy,($!lazy = 0)),
  2940. Rakudo::Iterator.Whatever($elem.iterator)
  2941. )
  2942. )
  2943. )
  2944. ),
  2945. self
  2946. )
  2947. }
  2948. method new(\iters,\map) { nqp::create(self)!SET-SELF(iters,map) }
  2949. method pull-one() {
  2950. nqp::if(
  2951. nqp::isnull($!iters),
  2952. IterationEnd,
  2953. nqp::stmts(
  2954. (my int $i = -1),
  2955. (my int $elems = nqp::elems($!iters)),
  2956. (my $list :=
  2957. nqp::setelems(nqp::create(IterationBuffer),$elems)),
  2958. nqp::until(
  2959. nqp::iseq_i(($i = nqp::add_i($i,1)),$elems)
  2960. || nqp::eqaddr(
  2961. (my $pulled := nqp::atpos($!iters,$i).pull-one),
  2962. IterationEnd
  2963. ),
  2964. nqp::bindpos($list,$i,$pulled)
  2965. ),
  2966. nqp::if(
  2967. nqp::islt_i($i,$elems), # at least one exhausted
  2968. nqp::stmts(
  2969. ($!iters := nqp::null),
  2970. IterationEnd
  2971. ),
  2972. $!mapper($list)
  2973. )
  2974. )
  2975. )
  2976. }
  2977. method is-lazy() { nqp::p6bool($!lazy) }
  2978. }.new(@iterables,&mapper),
  2979. nqp::if(
  2980. nqp::iseq_i($n,0),
  2981. Rakudo::Iterator.Empty,
  2982. nqp::atpos(nqp::getattr(@iterables,List,'$!reified'),0).iterator
  2983. )
  2984. )
  2985. }
  2986. # Return an iterator that will zip the given iterables and operator.
  2987. # Basically the functionality of @a Z=> @b, with &[=>] being the op.
  2988. method ZipIterablesOp(@iterables,\op) {
  2989. nqp::if(
  2990. nqp::eqaddr(op,&infix:<,>),
  2991. Rakudo::Iterator.ZipIterables(@iterables),
  2992. Rakudo::Iterator.ZipIterablesMap(
  2993. @iterables,
  2994. Rakudo::Metaops.MapperForOp(op)
  2995. )
  2996. )
  2997. }
  2998. }