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