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