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. my $laze := self.is-lazy;
  18. Seq.new(class :: does Iterator {
  19. has Iterator $!source;
  20. has Iterator $!nested;
  21. method new(\source) {
  22. nqp::p6bindattrinvres(nqp::create(self),self,'$!source',source)
  23. }
  24. my constant NO_RESULT_YET = nqp::create(Mu);
  25. method pull-one() is raw {
  26. my $result := NO_RESULT_YET;
  27. my $got;
  28. nqp::while(
  29. nqp::eqaddr($result, NO_RESULT_YET),
  30. nqp::if(
  31. $!nested,
  32. nqp::if(
  33. nqp::eqaddr(($got := $!nested.pull-one),IterationEnd),
  34. ($!nested := Iterator),
  35. ($result := $got)
  36. ),
  37. nqp::if(
  38. nqp::istype(($got := $!source.pull-one),Iterable)
  39. && nqp::not_i(nqp::iscont($got)),
  40. ($!nested := $got.flat.iterator),
  41. ($result := $got)
  42. )
  43. )
  44. );
  45. $result
  46. }
  47. method push-all($target --> IterationEnd) {
  48. my $iter := $!nested || $!source;
  49. nqp::until(
  50. nqp::eqaddr((my $got := $iter.pull-one), IterationEnd)
  51. && nqp::eqaddr($iter, $!source),
  52. nqp::if(
  53. nqp::eqaddr($got, IterationEnd),
  54. nqp::stmts(
  55. ($!nested := Iterator),
  56. ($iter := $!source),
  57. ),
  58. nqp::if(
  59. nqp::istype($got,Iterable)
  60. && nqp::not_i(nqp::iscont($got)),
  61. ($iter := $got.flat.iterator),
  62. $target.push($got),
  63. ),
  64. )
  65. );
  66. }
  67. }.new(self.iterator)).lazy-if($laze)
  68. }
  69. method lazy-if($flag) { $flag ?? self.lazy !! self }
  70. method lazy() {
  71. # Return a Seq with an iterator wrapping this Iterable, claiming to
  72. # be lazy, and implicitly preventing working ahead (by hiding any
  73. # push-at-least-n of the source iterator).
  74. Seq.new(class :: does Iterator {
  75. has $!iterable;
  76. has $!iterator;
  77. method new(\iterable) {
  78. nqp::p6bindattrinvres(
  79. nqp::create(self),self,'$!iterable',iterable
  80. )
  81. }
  82. method pull-one() is raw {
  83. $!iterator := $!iterable.iterator unless $!iterator.DEFINITE;
  84. $!iterator.pull-one
  85. }
  86. method push-exactly($target, int $n) {
  87. $!iterator := $!iterable.iterator unless $!iterator.DEFINITE;
  88. $!iterator.push-exactly($target, $n);
  89. }
  90. method is-lazy() { True }
  91. }.new(self))
  92. }
  93. method !valid-hyper-race($method,$batch,$degree --> Nil) {
  94. $batch <= 0
  95. ?? X::Invalid::Value.new(
  96. :$method,:name<batch>,:value($batch)).throw
  97. !! $degree <= 0
  98. ?? X::Invalid::Value.new(
  99. :$method,:name<degree>,:value($degree)).throw
  100. !! Nil
  101. }
  102. method hyper(Int(Cool) :$batch = 64, Int(Cool) :$degree = 4) {
  103. self!valid-hyper-race('hyper',$batch,$degree);
  104. self!go-hyper(HyperConfiguration.new(:!race, :$batch, :$degree))
  105. }
  106. method race(Int(Cool) :$batch = 64, Int(Cool) :$degree = 4) {
  107. self!valid-hyper-race('race',$batch,$degree);
  108. self!go-hyper(HyperConfiguration.new(:race, :$batch, :$degree))
  109. }
  110. method !go-hyper($configuration) {
  111. HyperSeq.new(class :: does HyperIterator {
  112. has $!source;
  113. has $!configuration;
  114. method new(\iter, $configuration) {
  115. my \hyper-iter = nqp::create(self);
  116. nqp::bindattr(hyper-iter, self, '$!source', iter);
  117. nqp::bindattr(hyper-iter, self, '$!configuration', $configuration);
  118. hyper-iter
  119. }
  120. method fill-buffer(HyperWorkBuffer:D $work, int $items) {
  121. $!source.push-exactly($work.input, $items)
  122. }
  123. method process-buffer(HyperWorkBuffer:D $work --> Nil) { }
  124. method configuration() { $!configuration }
  125. }.new(self.iterator, $configuration));
  126. }
  127. }