1. my class X::Seq::Consumed { ... }
  2. my class X::Seq::NotIndexable { ... }
  3. my class Seq is Cool does Iterable does Sequence {
  4. # The underlying iterator that iterating this sequence will work its
  5. # way through. Can only be obtained once.
  6. has Iterator $!iter;
  7. # The only valid way to create a Seq directly is by giving it the
  8. # iterator it will consume and maybe memoize.
  9. method new(Iterator:D $iter) {
  10. nqp::p6bindattrinvres(nqp::create(self),Seq,'$!iter',nqp::decont($iter))
  11. }
  12. method new-consumed() {
  13. self.bless;
  14. }
  15. method iterator(Seq:D:) {
  16. nqp::if(
  17. (my \iter = $!iter).DEFINITE,
  18. nqp::stmts(
  19. ($!iter := Iterator),
  20. iter
  21. ),
  22. nqp::if(
  23. $!list.DEFINITE,
  24. $!list.iterator,
  25. X::Seq::Consumed.new.throw
  26. )
  27. )
  28. }
  29. multi method is-lazy(Seq:D:) {
  30. nqp::if(
  31. $!iter.DEFINITE,
  32. $!iter.is-lazy,
  33. nqp::if(
  34. $!list.DEFINITE,
  35. $!list.is-lazy,
  36. X::Seq::Consumed.new.throw
  37. )
  38. )
  39. }
  40. multi method Seq(Seq:D:) { self }
  41. method Capture() {
  42. self.List.Capture
  43. }
  44. method elems() {
  45. nqp::if(
  46. self.is-lazy,
  47. Failure.new(X::Cannot::Lazy.new(action => '.elems')),
  48. nqp::if(
  49. ($!iter.DEFINITE && nqp::can($!iter,'count-only')),
  50. $!iter.count-only,
  51. self.cache.elems
  52. )
  53. )
  54. }
  55. method Numeric() {
  56. nqp::if(
  57. ($!iter.DEFINITE && nqp::can($!iter,'count-only')),
  58. $!iter.count-only,
  59. self.cache.Numeric
  60. )
  61. }
  62. method Int() {
  63. nqp::if(
  64. ($!iter.DEFINITE && nqp::can($!iter,'count-only')),
  65. $!iter.count-only,
  66. self.cache.Int
  67. )
  68. }
  69. method Bool(Seq:D:) {
  70. nqp::if(
  71. $!iter.DEFINITE,
  72. nqp::if(
  73. nqp::can($!iter,'bool-only'),
  74. $!iter.bool-only,
  75. nqp::if(
  76. nqp::can($!iter,'count-only'),
  77. ?$!iter.count-only,
  78. self.cache.Bool
  79. )
  80. ),
  81. self.cache.Bool
  82. )
  83. }
  84. multi method perl(Seq:D \SELF:) {
  85. # If we don't have an iterator, someone grabbed it already;
  86. # Check for cached $!list; if that's missing too, we're consumed
  87. my $perl;
  88. if not $!iter.DEFINITE and not $!list.DEFINITE {
  89. # cannot call .cache on a Seq that's already been iterated,
  90. # so we need to produce a string that, when EVAL'd, reproduces
  91. # an already iterated Seq.
  92. # compare RT #127492
  93. $perl = self.^name ~ '.new-consumed()';
  94. }
  95. else { $perl = self.cache.perl ~ '.Seq' }
  96. nqp::iscont(SELF) ?? '$(' ~ $perl ~ ')' !! $perl
  97. }
  98. method join(Seq:D: $separator = '' --> Str:D) {
  99. nqp::if(
  100. (my $iterator := self.iterator).is-lazy,
  101. '...',
  102. nqp::stmts(
  103. (my $strings := nqp::list_s),
  104. nqp::until(
  105. nqp::eqaddr((my $pulled := $iterator.pull-one),IterationEnd),
  106. nqp::push_s($strings,nqp::unbox_s(
  107. nqp::if(
  108. nqp::isconcrete($pulled) && nqp::istype($pulled,Str),
  109. $pulled,
  110. nqp::if(
  111. nqp::can($pulled,'Str'),
  112. $pulled.Str,
  113. nqp::box_s($pulled,Str)
  114. )
  115. )
  116. ))
  117. ),
  118. nqp::box_s(nqp::join(nqp::unbox_s($separator.Str),$strings),Str)
  119. )
  120. )
  121. }
  122. method sink(--> Nil) {
  123. nqp::if(
  124. $!iter.DEFINITE,
  125. nqp::stmts(
  126. $!iter.sink-all,
  127. ($!iter := Iterator)
  128. ),
  129. nqp::if(
  130. $!list.DEFINITE,
  131. $!list.sink
  132. )
  133. )
  134. }
  135. proto method from-loop(|) {*}
  136. multi method from-loop(&body) {
  137. Seq.new(Rakudo::Iterator.Loop(&body))
  138. }
  139. multi method from-loop(&body, &cond, :$repeat!) {
  140. Seq.new($repeat
  141. ?? Rakudo::Iterator.RepeatLoop(&body, &cond)
  142. !! Rakudo::Iterator.WhileLoop(&body, &cond)
  143. )
  144. }
  145. multi method from-loop(&body, &cond) {
  146. Seq.new(Rakudo::Iterator.WhileLoop(&body, &cond))
  147. }
  148. multi method from-loop(&body, &cond, &afterwards) {
  149. Seq.new(Rakudo::Iterator.CStyleLoop(&body, &cond, &afterwards))
  150. }
  151. }
  152. sub GATHER(&block) {
  153. Seq.new(class :: does SlippyIterator {
  154. has &!resumption;
  155. has $!push-target;
  156. has int $!wanted;
  157. my constant PROMPT = nqp::create(Mu);
  158. method new(&block) {
  159. my \iter = nqp::create(self);
  160. my int $wanted;
  161. my $taken;
  162. my $taker := {
  163. nqp::stmts(
  164. ($taken := nqp::getpayload(nqp::exception())),
  165. nqp::if(
  166. nqp::istype($taken, Slip),
  167. nqp::stmts(
  168. iter!start-slip-wanted($taken),
  169. ($wanted = nqp::getattr_i(iter, self, '$!wanted'))
  170. ),
  171. nqp::stmts( # doesn't sink
  172. nqp::getattr(iter, self, '$!push-target').push($taken),
  173. ($wanted = nqp::bindattr_i(iter,self,'$!wanted',
  174. nqp::sub_i(nqp::getattr_i(iter,self,'$!wanted'),1)))
  175. )
  176. ),
  177. nqp::if(
  178. nqp::iseq_i($wanted,0),
  179. nqp::continuationcontrol(0, PROMPT, -> Mu \c {
  180. nqp::bindattr(iter, self, '&!resumption', c);
  181. })
  182. ),
  183. nqp::resume(nqp::exception())
  184. )
  185. }
  186. nqp::bindattr(iter, self, '&!resumption', {
  187. nqp::stmts( # doesn't sink
  188. nqp::handle(&block(), 'TAKE', $taker()),
  189. nqp::continuationcontrol(0, PROMPT, -> | {
  190. nqp::bindattr(iter, self, '&!resumption', Callable)
  191. })
  192. )
  193. });
  194. iter
  195. }
  196. method pull-one() is raw {
  197. nqp::if(
  198. $!slipping && nqp::not_i(
  199. nqp::eqaddr((my \result = self.slip-one),IterationEnd)
  200. ),
  201. result,
  202. nqp::stmts(
  203. nqp::unless(
  204. $!push-target.DEFINITE,
  205. ($!push-target := nqp::create(IterationBuffer))
  206. ),
  207. ($!wanted = 1),
  208. nqp::continuationreset(PROMPT, &!resumption),
  209. nqp::if(
  210. &!resumption.DEFINITE,
  211. nqp::shift($!push-target),
  212. IterationEnd
  213. )
  214. )
  215. )
  216. }
  217. method push-exactly($target, int $n) {
  218. nqp::if(
  219. nqp::isgt_i($n,0),
  220. nqp::stmts(
  221. ($!wanted = $n),
  222. ($!push-target := $target),
  223. nqp::if(
  224. $!slipping && nqp::not_i(
  225. nqp::eqaddr(self!slip-wanted,IterationEnd)
  226. ),
  227. nqp::stmts(
  228. ($!push-target := nqp::null),
  229. $n
  230. ),
  231. nqp::stmts(
  232. nqp::continuationreset(PROMPT, &!resumption),
  233. ($!push-target := nqp::null),
  234. nqp::if(
  235. &!resumption.DEFINITE,
  236. ($n - $!wanted),
  237. IterationEnd
  238. )
  239. )
  240. )
  241. )
  242. )
  243. }
  244. method !start-slip-wanted(\slip --> Nil) {
  245. my $value := self.start-slip(slip);
  246. nqp::unless(
  247. nqp::eqaddr($value,IterationEnd),
  248. nqp::stmts( # doesn't sink
  249. $!push-target.push($value),
  250. (my int $i = 0),
  251. (my int $n = $!wanted),
  252. nqp::while( # doesn't sink
  253. nqp::islt_i($i = nqp::add_i($i,1),$n),
  254. nqp::if(
  255. nqp::eqaddr(($value := self.slip-one),IterationEnd),
  256. last
  257. ),
  258. $!push-target.push($value)
  259. ),
  260. ($!wanted = $!wanted - $i)
  261. )
  262. )
  263. }
  264. method !slip-wanted() {
  265. my int $i = -1;
  266. my int $n = $!wanted;
  267. my $value;
  268. nqp::while(
  269. nqp::islt_i($i = nqp::add_i($i,1),$n),
  270. nqp::stmts( # doesn't sink
  271. nqp::if(
  272. nqp::eqaddr(($value := self.slip-one),IterationEnd),
  273. last
  274. ),
  275. $!push-target.push($value)
  276. )
  277. );
  278. $!wanted = nqp::sub_i($!wanted,$i);
  279. nqp::if(
  280. nqp::eqaddr($value,IterationEnd),
  281. IterationEnd,
  282. $n
  283. )
  284. }
  285. }.new(&block))
  286. }
  287. multi sub infix:<eqv>(Seq:D \a, Seq:D \b) {
  288. nqp::p6bool(
  289. nqp::unless(
  290. nqp::eqaddr(a,b),
  291. nqp::if(
  292. nqp::eqaddr(a.WHAT,b.WHAT),
  293. nqp::if(
  294. nqp::iseq_i(
  295. (my \ia := a.iterator).is-lazy,
  296. (my \ib := b.iterator).is-lazy
  297. ),
  298. nqp::if(
  299. ia.is-lazy,
  300. die(X::Cannot::Lazy.new: :action<eqv>),
  301. nqp::stmts(
  302. nqp::until(
  303. nqp::stmts(
  304. (my \pa := ia.pull-one),
  305. (my \pb := ib.pull-one),
  306. nqp::eqaddr(pa,IterationEnd)
  307. || nqp::eqaddr(pb,IterationEnd)
  308. || nqp::not_i(pa eqv pb)
  309. ),
  310. nqp::null
  311. ),
  312. nqp::eqaddr(pa,pb) # exhausted if both IterationEnd
  313. )
  314. )
  315. )
  316. )
  317. )
  318. )
  319. }
  320. # The Empty Sequence
  321. my constant EmptySeq = nqp::p6bindattrinvres(
  322. nqp::create( class EmptySeq is Seq {
  323. method iterator() { nqp::getattr(self,Seq,'$!iter') }
  324. }),Seq,'$!iter',Rakudo::Iterator.Empty);