1. # A Seq represents anything that can lazily produce a sequence of values. A
  2. # Seq is born in a state where iterating it will consume the values. However,
  3. # calling .cache on a Seq will return a List that will lazily reify to the
  4. # values in the Seq. The List is memoized, so that subsequent calls to .cache
  5. # will always return the same List (safe thanks to List being immutable). More
  6. # than one call to .iterator throws an exception (and calling .cache calls the
  7. # .iterator method the first time also). The memoization can be avoided by
  8. # asking very specifically for the Seq to be coerced to a List (using .List or .list), a
  9. # Slip (.Slip) or an Array (.Array). The actual memoization functionality is
  10. # factored out into a role, PositionalBindFailover, which is used by the binder
  11. # to identify types that, on failure to bind to an @-sigilled thing, can have
  12. # .cache called on them and expect memoization semantics. This not only makes
  13. # it easy for HyperSeq to also have this functionality, but makes it available
  14. # for other kinds of paradigm that show up in the future (beyond sequential
  15. # and parallel) that also want to have this behavior.
  16. my $in_deprecation;
  17. my class X::Seq::Consumed { ... }
  18. my class X::Seq::NotIndexable { ... }
  19. my role PositionalBindFailover {
  20. has $!list;
  21. method cache() {
  22. $!list.DEFINITE
  23. ?? $!list
  24. !! ($!list := List.from-iterator(self.iterator))
  25. }
  26. method list() {
  27. List.from-iterator(self.iterator)
  28. }
  29. method iterator() { ... }
  30. }
  31. nqp::p6configposbindfailover(Positional, PositionalBindFailover); # Binder
  32. Routine.'!configure_positional_bind_failover'(Positional, PositionalBindFailover); # Multi-dispatch
  33. my class Seq is Cool does Iterable does PositionalBindFailover {
  34. # The underlying iterator that iterating this sequence will work its
  35. # way through. Can only be obtained once.
  36. has Iterator $!iter;
  37. # The only valid way to create a Seq directly is by giving it the
  38. # iterator it will consume and maybe memoize.
  39. method new(Iterator:D $iter) {
  40. nqp::p6bindattrinvres(nqp::create(self),Seq,'$!iter',nqp::decont($iter))
  41. }
  42. method new-consumed() {
  43. self.bless;
  44. }
  45. method is-ready(Seq:D:) { $!iter.DEFINITE }
  46. method iterator(Seq:D:) {
  47. nqp::if(
  48. (my \iter = $!iter).DEFINITE,
  49. nqp::stmts(
  50. ($!iter := Iterator),
  51. iter
  52. ),
  53. X::Seq::Consumed.new.throw
  54. )
  55. }
  56. multi method is-lazy(Seq:D:) {
  57. nqp::if(
  58. $!iter.DEFINITE,
  59. $!iter.is-lazy,
  60. X::Seq::Consumed.new.throw
  61. )
  62. }
  63. method eager {
  64. List.from-iterator(self.iterator).eager;
  65. }
  66. method List() {
  67. List.from-iterator(self.iterator)
  68. }
  69. method Slip() {
  70. Slip.from-iterator(self.iterator)
  71. }
  72. method Array() {
  73. Array.from-iterator(self.iterator)
  74. }
  75. method elems() {
  76. nqp::if(
  77. self.is-lazy,
  78. Failure.new(X::Cannot::Lazy.new(action => '.elems')),
  79. nqp::if(
  80. ($!iter.DEFINITE && nqp::can($!iter,'count-only')),
  81. $!iter.count-only,
  82. self.cache.elems
  83. )
  84. )
  85. }
  86. method Numeric() {
  87. nqp::if(
  88. ($!iter.DEFINITE && nqp::can($!iter,'count-only')),
  89. $!iter.count-only,
  90. self.cache.Numeric
  91. )
  92. }
  93. method Int() {
  94. nqp::if(
  95. ($!iter.DEFINITE && nqp::can($!iter,'count-only')),
  96. $!iter.count-only,
  97. self.cache.Int
  98. )
  99. }
  100. method Bool(Seq:D:) {
  101. nqp::if(
  102. $!iter.DEFINITE,
  103. nqp::if(
  104. nqp::can($!iter,'bool-only'),
  105. $!iter.bool-only,
  106. nqp::if(
  107. nqp::can($!iter,'count-only'),
  108. ?$!iter.count-only,
  109. self.cache.Bool
  110. )
  111. ),
  112. self.cache.Bool
  113. )
  114. }
  115. multi method Str(Seq:D:) {
  116. self.cache.Str
  117. }
  118. multi method Stringy(Seq:D:) {
  119. self.cache.Stringy
  120. }
  121. method fmt(|c) {
  122. self.cache.fmt(|c)
  123. }
  124. multi method gist(Seq:D:) {
  125. self.cache.gist
  126. }
  127. multi method perl(Seq:D \SELF:) {
  128. unless $!iter.DEFINITE && ! $!list.DEFINITE {
  129. # cannot call .cache on a Seq that's already been iterated,
  130. # so we need to produce a string that, when EVAL'd, reproduces
  131. # an already iterated Seq.
  132. # compare RT #127492
  133. return self.^name ~ '.new-consumed()';
  134. }
  135. self.cache.perl ~ '.Seq';
  136. }
  137. method join(Seq:D: $separator = '' --> Str:D) {
  138. nqp::if(
  139. (my $iterator := self.iterator).is-lazy,
  140. '...',
  141. nqp::stmts(
  142. (my $strings := nqp::list_s),
  143. nqp::until(
  144. nqp::eqaddr((my $pulled := $iterator.pull-one),IterationEnd),
  145. nqp::push_s($strings,nqp::unbox_s(
  146. nqp::if(
  147. nqp::isconcrete($pulled) && nqp::istype($pulled,Str),
  148. $pulled,
  149. nqp::if(
  150. nqp::can($pulled,'Str'),
  151. $pulled.Str,
  152. nqp::box_s($pulled,Str)
  153. )
  154. )
  155. ))
  156. ),
  157. nqp::box_s(nqp::join(nqp::unbox_s($separator.Str),$strings),Str)
  158. )
  159. )
  160. }
  161. method sink(--> Nil) {
  162. nqp::if(
  163. $!iter.DEFINITE,
  164. nqp::stmts(
  165. $!iter.sink-all,
  166. ($!iter := Iterator)
  167. )
  168. )
  169. }
  170. multi method AT-POS(Seq:D: Int $idx) is raw {
  171. self.cache.AT-POS($idx)
  172. }
  173. multi method AT-POS(Seq:D: int $idx) is raw {
  174. self.cache.AT-POS($idx)
  175. }
  176. multi method EXISTS-POS(Seq:D: Int $idx) {
  177. self.cache.EXISTS-POS($idx)
  178. }
  179. multi method EXISTS-POS(Seq:D: int $idx) {
  180. self.cache.EXISTS-POS($idx)
  181. }
  182. proto method from-loop(|) { * }
  183. multi method from-loop(&body) {
  184. Seq.new(Rakudo::Iterator.Loop(&body))
  185. }
  186. multi method from-loop(&body, &cond, :$repeat!) {
  187. Seq.new($repeat
  188. ?? Rakudo::Iterator.RepeatLoop(&body, &cond)
  189. !! Rakudo::Iterator.WhileLoop(&body, &cond)
  190. )
  191. }
  192. multi method from-loop(&body, &cond) {
  193. Seq.new(Rakudo::Iterator.WhileLoop(&body, &cond))
  194. }
  195. multi method from-loop(&body, &cond, &afterwards) {
  196. Seq.new(Rakudo::Iterator.CStyleLoop(&body, &cond, &afterwards))
  197. }
  198. multi method skip() { nqp::stmts( $!iter.skip-one, self) }
  199. multi method skip(Int() $n) { nqp::stmts( $!iter.skip-at-least($n), self) }
  200. }
  201. sub GATHER(&block) {
  202. Seq.new(class :: does SlippyIterator {
  203. has &!resumption;
  204. has $!push-target;
  205. has int $!wanted;
  206. my constant PROMPT = nqp::create(Mu);
  207. method new(&block) {
  208. my \iter = nqp::create(self);
  209. my int $wanted;
  210. my $taken;
  211. my $taker := {
  212. nqp::stmts(
  213. ($taken := nqp::getpayload(nqp::exception())),
  214. nqp::if(
  215. nqp::istype($taken, Slip),
  216. nqp::stmts(
  217. iter!start-slip-wanted($taken),
  218. ($wanted = nqp::getattr_i(iter, self, '$!wanted'))
  219. ),
  220. nqp::stmts( # doesn't sink
  221. nqp::getattr(iter, self, '$!push-target').push($taken),
  222. ($wanted = nqp::bindattr_i(iter,self,'$!wanted',
  223. nqp::sub_i(nqp::getattr_i(iter,self,'$!wanted'),1)))
  224. )
  225. ),
  226. nqp::if(
  227. nqp::iseq_i($wanted,0),
  228. nqp::continuationcontrol(0, PROMPT, -> Mu \c {
  229. nqp::bindattr(iter, self, '&!resumption', c);
  230. })
  231. ),
  232. nqp::resume(nqp::exception())
  233. )
  234. }
  235. nqp::bindattr(iter, self, '&!resumption', {
  236. nqp::stmts( # doesn't sink
  237. nqp::handle(&block(), 'TAKE', $taker()),
  238. nqp::continuationcontrol(0, PROMPT, -> | {
  239. nqp::bindattr(iter, self, '&!resumption', Callable)
  240. })
  241. )
  242. });
  243. iter
  244. }
  245. method pull-one() is raw {
  246. nqp::if(
  247. $!slipping && nqp::not_i(
  248. nqp::eqaddr((my \result = self.slip-one),IterationEnd)
  249. ),
  250. result,
  251. nqp::stmts(
  252. nqp::unless(
  253. $!push-target.DEFINITE,
  254. ($!push-target := nqp::create(IterationBuffer))
  255. ),
  256. ($!wanted = 1),
  257. nqp::continuationreset(PROMPT, &!resumption),
  258. nqp::if(
  259. &!resumption.DEFINITE,
  260. nqp::shift($!push-target),
  261. IterationEnd
  262. )
  263. )
  264. )
  265. }
  266. method push-exactly($target, int $n) {
  267. nqp::if(
  268. nqp::isgt_i($n,0),
  269. nqp::stmts(
  270. ($!wanted = $n),
  271. ($!push-target := $target),
  272. nqp::if(
  273. $!slipping && nqp::not_i(
  274. nqp::eqaddr(self!slip-wanted,IterationEnd)
  275. ),
  276. nqp::stmts(
  277. ($!push-target := nqp::null),
  278. $n
  279. ),
  280. nqp::stmts(
  281. nqp::continuationreset(PROMPT, &!resumption),
  282. ($!push-target := nqp::null),
  283. nqp::if(
  284. &!resumption.DEFINITE,
  285. ($n - $!wanted),
  286. IterationEnd
  287. )
  288. )
  289. )
  290. )
  291. )
  292. }
  293. method !start-slip-wanted(\slip --> Nil) {
  294. my $value := self.start-slip(slip);
  295. nqp::unless(
  296. nqp::eqaddr($value,IterationEnd),
  297. nqp::stmts( # doesn't sink
  298. $!push-target.push($value),
  299. (my int $i = 0),
  300. (my int $n = $!wanted),
  301. nqp::while( # doesn't sink
  302. nqp::islt_i($i = nqp::add_i($i,1),$n),
  303. nqp::if(
  304. nqp::eqaddr(($value := self.slip-one),IterationEnd),
  305. last
  306. ),
  307. $!push-target.push($value)
  308. ),
  309. ($!wanted = $!wanted - $i)
  310. )
  311. )
  312. }
  313. method !slip-wanted() {
  314. my int $i = -1;
  315. my int $n = $!wanted;
  316. my $value;
  317. nqp::while(
  318. nqp::islt_i($i = nqp::add_i($i,1),$n),
  319. nqp::stmts( # doesn't sink
  320. nqp::if(
  321. nqp::eqaddr(($value := self.slip-one),IterationEnd),
  322. last
  323. ),
  324. $!push-target.push($value)
  325. )
  326. );
  327. $!wanted = nqp::sub_i($!wanted,$i);
  328. nqp::if(
  329. nqp::eqaddr($value,IterationEnd),
  330. IterationEnd,
  331. $n
  332. )
  333. }
  334. }.new(&block))
  335. }
  336. multi sub infix:<eqv>(Seq:D \a, Seq:D \b) {
  337. nqp::p6bool(
  338. nqp::unless(
  339. nqp::eqaddr(a,b),
  340. nqp::if(
  341. nqp::eqaddr(a.WHAT,b.WHAT),
  342. nqp::if(
  343. nqp::iseq_i(
  344. (my \ia := a.iterator).is-lazy,
  345. (my \ib := b.iterator).is-lazy
  346. ),
  347. nqp::if(
  348. ia.is-lazy,
  349. (die "Cannot eqv lazy Sequences"),
  350. nqp::stmts(
  351. nqp::until(
  352. nqp::stmts(
  353. (my \pa := ia.pull-one),
  354. (my \pb := ib.pull-one),
  355. nqp::eqaddr(pa,IterationEnd)
  356. || nqp::eqaddr(pb,IterationEnd)
  357. || nqp::not_i(pa eqv pb)
  358. ),
  359. nqp::null
  360. ),
  361. nqp::eqaddr(pa,pb) # exhausted if both IterationEnd
  362. )
  363. )
  364. )
  365. )
  366. )
  367. )
  368. }