1. # Iterable is done by anything that we should be able to get an iterator
  2. # from. Things that are Iterable will flatten in flattening contexts, so a
  3. # default implementation of .flat is provided by this role. As itemization is
  4. # what defeats flattening, this role also provides a default .item method.
  5. # Additionally, as .lazy and .eager are about iterator behavior, they are
  6. # provided by this role. Overriding those is not likely to be needed, and
  7. # discouraged to maintain predictable semantics. Finally, both .hyper() and
  8. # .race() are implemented here, and return a HyperSeq wrapping the iterator.
  9. my class HyperSeq { ... }
  10. my class X::Invalid::Value { ... }
  11. my role Iterable {
  12. method iterator() { ... }
  13. method item() {
  14. nqp::p6bindattrinvres(nqp::create(Scalar), Scalar, '$!value', self)
  15. }
  16. method flat(Iterable:D:) {
  17. Seq.new(class :: does Iterator {
  18. has Iterator $!source;
  19. has Iterator $!nested;
  20. method new(\source) {
  21. nqp::p6bindattrinvres(nqp::create(self),self,'$!source',source)
  22. }
  23. method pull-one() is raw {
  24. nqp::if(
  25. $!nested,
  26. nqp::if(
  27. nqp::eqaddr((my $got := $!nested.pull-one),IterationEnd),
  28. nqp::stmts(
  29. ($!nested := Iterator),
  30. self.pull-one
  31. ),
  32. $got
  33. ),
  34. nqp::if(
  35. nqp::iscont($got := $!source.pull-one),
  36. $got,
  37. nqp::if(
  38. nqp::istype($got,Iterable),
  39. nqp::stmts(
  40. ($!nested := $got.flat.iterator),
  41. self.pull-one
  42. ),
  43. $got
  44. )
  45. )
  46. )
  47. }
  48. method push-all($target --> IterationEnd) {
  49. nqp::stmts(
  50. nqp::if(
  51. $!nested,
  52. nqp::stmts(
  53. $!nested.push-all($target),
  54. ($!nested := Iterator)
  55. )
  56. ),
  57. nqp::until(
  58. nqp::eqaddr((my $got := $!source.pull-one), IterationEnd),
  59. nqp::if(
  60. nqp::iscont($got),
  61. $target.push($got),
  62. nqp::if(
  63. nqp::istype($got,Iterable),
  64. $got.flat.iterator.push-all($target),
  65. $target.push($got)
  66. )
  67. )
  68. )
  69. )
  70. }
  71. method is-lazy() { $!source.is-lazy }
  72. }.new(self.iterator))
  73. }
  74. method lazy-if($flag) { $flag ?? self.lazy !! self }
  75. method lazy() {
  76. # Return a Seq with an iterator wrapping this Iterable, claiming to
  77. # be lazy, and implicitly preventing working ahead (by hiding any
  78. # push-at-least-n of the source iterator).
  79. Seq.new(class :: does Iterator {
  80. has $!iterable;
  81. has $!iterator;
  82. method new(\iterable) {
  83. nqp::p6bindattrinvres(
  84. nqp::create(self),self,'$!iterable',iterable
  85. )
  86. }
  87. method pull-one() is raw {
  88. $!iterator := $!iterable.iterator unless $!iterator.DEFINITE;
  89. $!iterator.pull-one
  90. }
  91. method push-exactly($target, int $n) {
  92. $!iterator := $!iterable.iterator unless $!iterator.DEFINITE;
  93. $!iterator.push-exactly($target, $n);
  94. }
  95. method is-lazy() { True }
  96. }.new(self))
  97. }
  98. method !valid-hyper-race($method,$batch,$degree --> Nil) {
  99. $batch <= 0
  100. ?? X::Invalid::Value.new(
  101. :$method,:name<batch>,:value($batch)).throw
  102. !! $degree <= 0
  103. ?? X::Invalid::Value.new(
  104. :$method,:name<degree>,:value($degree)).throw
  105. !! Nil
  106. }
  107. method hyper(Int(Cool) :$batch = 64, Int(Cool) :$degree = 4) {
  108. self!valid-hyper-race('hyper',$batch,$degree);
  109. self!go-hyper(HyperConfiguration.new(:!race, :$batch, :$degree))
  110. }
  111. method race(Int(Cool) :$batch = 64, Int(Cool) :$degree = 4) {
  112. self!valid-hyper-race('race',$batch,$degree);
  113. self!go-hyper(HyperConfiguration.new(:race, :$batch, :$degree))
  114. }
  115. method !go-hyper($configuration) {
  116. HyperSeq.new(class :: does HyperIterator {
  117. has $!source;
  118. has $!configuration;
  119. method new(\iter, $configuration) {
  120. my \hyper-iter = nqp::create(self);
  121. nqp::bindattr(hyper-iter, self, '$!source', iter);
  122. nqp::bindattr(hyper-iter, self, '$!configuration', $configuration);
  123. hyper-iter
  124. }
  125. method fill-buffer(HyperWorkBuffer:D $work, int $items) {
  126. $!source.push-exactly($work.input, $items)
  127. }
  128. method process-buffer(HyperWorkBuffer:D $work --> Nil) { }
  129. method configuration() { $!configuration }
  130. }.new(self.iterator, $configuration));
  131. }
  132. method !SETIFY(\type) {
  133. nqp::create(type).SET-SELF(
  134. type.fill_IterationSet(
  135. nqp::create(Rakudo::Internals::IterationSet),self.flat.iterator
  136. )
  137. )
  138. }
  139. multi method Set(Iterable:D:) { self!SETIFY(Set) }
  140. multi method SetHash(Iterable:D:) { self!SETIFY(SetHash) }
  141. }