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 iterator(Seq:D:) {
  46. nqp::if(
  47. (my \iter = $!iter).DEFINITE,
  48. nqp::stmts(
  49. ($!iter := Iterator),
  50. iter
  51. ),
  52. nqp::if(
  53. $!list.DEFINITE,
  54. $!list.iterator,
  55. X::Seq::Consumed.new.throw
  56. )
  57. )
  58. }
  59. multi method is-lazy(Seq:D:) {
  60. nqp::if(
  61. $!iter.DEFINITE,
  62. $!iter.is-lazy,
  63. nqp::if(
  64. $!list.DEFINITE,
  65. $!list.is-lazy,
  66. X::Seq::Consumed.new.throw
  67. )
  68. )
  69. }
  70. method eager {
  71. List.from-iterator(self.iterator).eager;
  72. }
  73. method Capture() {
  74. self.List.Capture
  75. }
  76. method List() {
  77. List.from-iterator(self.iterator)
  78. }
  79. method Slip() {
  80. Slip.from-iterator(self.iterator)
  81. }
  82. method Array() {
  83. Array.from-iterator(self.iterator)
  84. }
  85. method elems() {
  86. nqp::if(
  87. self.is-lazy,
  88. Failure.new(X::Cannot::Lazy.new(action => '.elems')),
  89. nqp::if(
  90. ($!iter.DEFINITE && nqp::can($!iter,'count-only')),
  91. $!iter.count-only,
  92. self.cache.elems
  93. )
  94. )
  95. }
  96. method Numeric() {
  97. nqp::if(
  98. ($!iter.DEFINITE && nqp::can($!iter,'count-only')),
  99. $!iter.count-only,
  100. self.cache.Numeric
  101. )
  102. }
  103. method Int() {
  104. nqp::if(
  105. ($!iter.DEFINITE && nqp::can($!iter,'count-only')),
  106. $!iter.count-only,
  107. self.cache.Int
  108. )
  109. }
  110. method Bool(Seq:D:) {
  111. nqp::if(
  112. $!iter.DEFINITE,
  113. nqp::if(
  114. nqp::can($!iter,'bool-only'),
  115. $!iter.bool-only,
  116. nqp::if(
  117. nqp::can($!iter,'count-only'),
  118. ?$!iter.count-only,
  119. self.cache.Bool
  120. )
  121. ),
  122. self.cache.Bool
  123. )
  124. }
  125. multi method Str(Seq:D:) {
  126. self.cache.Str
  127. }
  128. multi method Stringy(Seq:D:) {
  129. self.cache.Stringy
  130. }
  131. method fmt(|c) {
  132. self.cache.fmt(|c)
  133. }
  134. multi method gist(Seq:D:) {
  135. self.cache.gist
  136. }
  137. multi method perl(Seq:D \SELF:) {
  138. # If we don't have an iterator, someone grabbed it already;
  139. # Check for cached $!list; if that's missing too, we're consumed
  140. if not $!iter.DEFINITE and not $!list.DEFINITE {
  141. # cannot call .cache on a Seq that's already been iterated,
  142. # so we need to produce a string that, when EVAL'd, reproduces
  143. # an already iterated Seq.
  144. # compare RT #127492
  145. return self.^name ~ '.new-consumed()';
  146. }
  147. self.cache.perl ~ '.Seq';
  148. }
  149. method join(Seq:D: $separator = '' --> Str:D) {
  150. nqp::if(
  151. (my $iterator := self.iterator).is-lazy,
  152. '...',
  153. nqp::stmts(
  154. (my $strings := nqp::list_s),
  155. nqp::until(
  156. nqp::eqaddr((my $pulled := $iterator.pull-one),IterationEnd),
  157. nqp::push_s($strings,nqp::unbox_s(
  158. nqp::if(
  159. nqp::isconcrete($pulled) && nqp::istype($pulled,Str),
  160. $pulled,
  161. nqp::if(
  162. nqp::can($pulled,'Str'),
  163. $pulled.Str,
  164. nqp::box_s($pulled,Str)
  165. )
  166. )
  167. ))
  168. ),
  169. nqp::box_s(nqp::join(nqp::unbox_s($separator.Str),$strings),Str)
  170. )
  171. )
  172. }
  173. method sink(--> Nil) {
  174. nqp::if(
  175. $!iter.DEFINITE,
  176. nqp::stmts(
  177. $!iter.sink-all,
  178. ($!iter := Iterator)
  179. ),
  180. nqp::if(
  181. $!list.DEFINITE,
  182. $!list.sink
  183. )
  184. )
  185. }
  186. multi method AT-POS(Seq:D: Int $idx) is raw {
  187. self.cache.AT-POS($idx)
  188. }
  189. multi method AT-POS(Seq:D: int $idx) is raw {
  190. self.cache.AT-POS($idx)
  191. }
  192. multi method EXISTS-POS(Seq:D: Int $idx) {
  193. self.cache.EXISTS-POS($idx)
  194. }
  195. multi method EXISTS-POS(Seq:D: int $idx) {
  196. self.cache.EXISTS-POS($idx)
  197. }
  198. proto method from-loop(|) { * }
  199. multi method from-loop(&body) {
  200. Seq.new(Rakudo::Iterator.Loop(&body))
  201. }
  202. multi method from-loop(&body, &cond, :$repeat!) {
  203. Seq.new($repeat
  204. ?? Rakudo::Iterator.RepeatLoop(&body, &cond)
  205. !! Rakudo::Iterator.WhileLoop(&body, &cond)
  206. )
  207. }
  208. multi method from-loop(&body, &cond) {
  209. Seq.new(Rakudo::Iterator.WhileLoop(&body, &cond))
  210. }
  211. multi method from-loop(&body, &cond, &afterwards) {
  212. Seq.new(Rakudo::Iterator.CStyleLoop(&body, &cond, &afterwards))
  213. }
  214. multi method skip() { nqp::stmts( $!iter.skip-one, self) }
  215. multi method skip(Int() $n) { nqp::stmts( $!iter.skip-at-least($n), self) }
  216. }
  217. sub GATHER(&block) {
  218. Seq.new(class :: does SlippyIterator {
  219. has &!resumption;
  220. has $!push-target;
  221. has int $!wanted;
  222. my constant PROMPT = nqp::create(Mu);
  223. method new(&block) {
  224. my \iter = nqp::create(self);
  225. my int $wanted;
  226. my $taken;
  227. my $taker := {
  228. nqp::stmts(
  229. ($taken := nqp::getpayload(nqp::exception())),
  230. nqp::if(
  231. nqp::istype($taken, Slip),
  232. nqp::stmts(
  233. iter!start-slip-wanted($taken),
  234. ($wanted = nqp::getattr_i(iter, self, '$!wanted'))
  235. ),
  236. nqp::stmts( # doesn't sink
  237. nqp::getattr(iter, self, '$!push-target').push($taken),
  238. ($wanted = nqp::bindattr_i(iter,self,'$!wanted',
  239. nqp::sub_i(nqp::getattr_i(iter,self,'$!wanted'),1)))
  240. )
  241. ),
  242. nqp::if(
  243. nqp::iseq_i($wanted,0),
  244. nqp::continuationcontrol(0, PROMPT, -> Mu \c {
  245. nqp::bindattr(iter, self, '&!resumption', c);
  246. })
  247. ),
  248. nqp::resume(nqp::exception())
  249. )
  250. }
  251. nqp::bindattr(iter, self, '&!resumption', {
  252. nqp::stmts( # doesn't sink
  253. nqp::handle(&block(), 'TAKE', $taker()),
  254. nqp::continuationcontrol(0, PROMPT, -> | {
  255. nqp::bindattr(iter, self, '&!resumption', Callable)
  256. })
  257. )
  258. });
  259. iter
  260. }
  261. method pull-one() is raw {
  262. nqp::if(
  263. $!slipping && nqp::not_i(
  264. nqp::eqaddr((my \result = self.slip-one),IterationEnd)
  265. ),
  266. result,
  267. nqp::stmts(
  268. nqp::unless(
  269. $!push-target.DEFINITE,
  270. ($!push-target := nqp::create(IterationBuffer))
  271. ),
  272. ($!wanted = 1),
  273. nqp::continuationreset(PROMPT, &!resumption),
  274. nqp::if(
  275. &!resumption.DEFINITE,
  276. nqp::shift($!push-target),
  277. IterationEnd
  278. )
  279. )
  280. )
  281. }
  282. method push-exactly($target, int $n) {
  283. nqp::if(
  284. nqp::isgt_i($n,0),
  285. nqp::stmts(
  286. ($!wanted = $n),
  287. ($!push-target := $target),
  288. nqp::if(
  289. $!slipping && nqp::not_i(
  290. nqp::eqaddr(self!slip-wanted,IterationEnd)
  291. ),
  292. nqp::stmts(
  293. ($!push-target := nqp::null),
  294. $n
  295. ),
  296. nqp::stmts(
  297. nqp::continuationreset(PROMPT, &!resumption),
  298. ($!push-target := nqp::null),
  299. nqp::if(
  300. &!resumption.DEFINITE,
  301. ($n - $!wanted),
  302. IterationEnd
  303. )
  304. )
  305. )
  306. )
  307. )
  308. }
  309. method !start-slip-wanted(\slip --> Nil) {
  310. my $value := self.start-slip(slip);
  311. nqp::unless(
  312. nqp::eqaddr($value,IterationEnd),
  313. nqp::stmts( # doesn't sink
  314. $!push-target.push($value),
  315. (my int $i = 0),
  316. (my int $n = $!wanted),
  317. nqp::while( # doesn't sink
  318. nqp::islt_i($i = nqp::add_i($i,1),$n),
  319. nqp::if(
  320. nqp::eqaddr(($value := self.slip-one),IterationEnd),
  321. last
  322. ),
  323. $!push-target.push($value)
  324. ),
  325. ($!wanted = $!wanted - $i)
  326. )
  327. )
  328. }
  329. method !slip-wanted() {
  330. my int $i = -1;
  331. my int $n = $!wanted;
  332. my $value;
  333. nqp::while(
  334. nqp::islt_i($i = nqp::add_i($i,1),$n),
  335. nqp::stmts( # doesn't sink
  336. nqp::if(
  337. nqp::eqaddr(($value := self.slip-one),IterationEnd),
  338. last
  339. ),
  340. $!push-target.push($value)
  341. )
  342. );
  343. $!wanted = nqp::sub_i($!wanted,$i);
  344. nqp::if(
  345. nqp::eqaddr($value,IterationEnd),
  346. IterationEnd,
  347. $n
  348. )
  349. }
  350. }.new(&block))
  351. }
  352. multi sub infix:<eqv>(Seq:D \a, Seq:D \b) {
  353. nqp::p6bool(
  354. nqp::unless(
  355. nqp::eqaddr(a,b),
  356. nqp::if(
  357. nqp::eqaddr(a.WHAT,b.WHAT),
  358. nqp::if(
  359. nqp::iseq_i(
  360. (my \ia := a.iterator).is-lazy,
  361. (my \ib := b.iterator).is-lazy
  362. ),
  363. nqp::if(
  364. ia.is-lazy,
  365. (die "Cannot eqv lazy Sequences"),
  366. nqp::stmts(
  367. nqp::until(
  368. nqp::stmts(
  369. (my \pa := ia.pull-one),
  370. (my \pb := ib.pull-one),
  371. nqp::eqaddr(pa,IterationEnd)
  372. || nqp::eqaddr(pb,IterationEnd)
  373. || nqp::not_i(pa eqv pb)
  374. ),
  375. nqp::null
  376. ),
  377. nqp::eqaddr(pa,pb) # exhausted if both IterationEnd
  378. )
  379. )
  380. )
  381. )
  382. )
  383. )
  384. }