1. # Now that Iterable is defined, we add extra methods into Any for the list
  2. # operations. (They can't go into Any right away since we need Attribute to
  3. # define the various roles, and Attribute inherits from Any. We will do a
  4. # re-compose of Attribute to make sure it gets the list methods at the end
  5. # of this file. Note the general pattern for these list-y methods is that
  6. # they check if they have an Iterable already, and if not obtain one to
  7. # work on by doing a .list coercion.
  8. use MONKEY-TYPING;
  9. augment class Any {
  10. proto method map(|) is nodal { * }
  11. multi method map(Hash \h) {
  12. die "Cannot map a {self.^name} to a {h.^name}.
  13. Did you mean to add a stub (\{...\}) or did you mean to .classify?"
  14. }
  15. multi method map(\SELF: █; :$label, :$item) {
  16. sequential-map(($item ?? (SELF,) !! SELF).iterator, &block, $label);
  17. }
  18. multi method map(HyperIterable:D: █; :$label) {
  19. # For now we only know how to parallelize when we've only one input
  20. # value needed per block. For the rest, fall back to sequential.
  21. if &block.count != 1 {
  22. sequential-map(self.iterator, &block, $label)
  23. }
  24. else {
  25. HyperSeq.new(class :: does HyperIterator {
  26. has $!source;
  27. has &!block;
  28. method new(\source, &block) {
  29. my \iter = nqp::create(self);
  30. nqp::bindattr(iter, self, '$!source', source);
  31. nqp::bindattr(iter, self, '&!block', &block);
  32. iter
  33. }
  34. method fill-buffer(HyperWorkBuffer:D $work, int $items) {
  35. $!source.fill-buffer($work, $items);
  36. }
  37. method process-buffer(HyperWorkBuffer:D $work) {
  38. unless $!source.process-buffer($work) =:= Nil {
  39. $work.swap();
  40. }
  41. my \buffer-mapper = sequential-map($work.input-iterator, &!block, $label);
  42. buffer-mapper.iterator.push-all($work.output);
  43. $work
  44. }
  45. method configuration() {
  46. $!source.configuration
  47. }
  48. }.new(self.hyper-iterator, &block))
  49. }
  50. }
  51. my class IterateOneWithPhasers does SlippyIterator {
  52. has &!block;
  53. has $!source;
  54. has $!label;
  55. has Int $!NEXT; # SHOULD BE int, but has Int performs better
  56. has Int $!did-init; # SHOULD BE int, but has Int performs better
  57. has Int $!did-iterate; # SHOULD BE int, but has Int performs better
  58. method !SET-SELF(\block,\source,\label) {
  59. nqp::stmts(
  60. (&!block := block),
  61. ($!source := source),
  62. ($!label := label),
  63. ($!NEXT = block.has-phaser('NEXT')),
  64. self
  65. )
  66. }
  67. method new(\bl,\sou,\la) { nqp::create(self)!SET-SELF(bl,sou,la) }
  68. method is-lazy() { $!source.is-lazy }
  69. method pull-one() is raw {
  70. my int $stopped;
  71. my $value;
  72. my $result;
  73. nqp::unless(
  74. $!did-init,
  75. nqp::stmts(
  76. ($!did-init = 1),
  77. nqp::if(
  78. &!block.has-phaser('FIRST'),
  79. nqp::p6setfirstflag(&!block)
  80. )
  81. )
  82. );
  83. if $!slipping && nqp::not_i(nqp::eqaddr(($result := self.slip-one),IterationEnd)) {
  84. # $result will be returned at the end
  85. }
  86. elsif nqp::eqaddr(($value := $!source.pull-one),IterationEnd) {
  87. $result := IterationEnd
  88. }
  89. else {
  90. nqp::until(
  91. $stopped,
  92. nqp::handle(
  93. nqp::stmts(
  94. ($stopped = 1),
  95. ($result := &!block($value)),
  96. ($!did-iterate = 1),
  97. nqp::if(
  98. nqp::istype($result, Slip),
  99. nqp::if(
  100. nqp::eqaddr(($result := self.start-slip($result)), IterationEnd),
  101. nqp::if(
  102. nqp::not_i(nqp::eqaddr(($value := $!source.pull-one),IterationEnd)),
  103. ($stopped = 0)
  104. ),
  105. )
  106. ),
  107. nqp::if($!NEXT, &!block.fire_phasers('NEXT')),
  108. ),
  109. 'LABELED', $!label,
  110. 'NEXT', nqp::stmts(
  111. ($!did-iterate = 1),
  112. nqp::if($!NEXT, &!block.fire_phasers('NEXT')),
  113. nqp::eqaddr(($value := $!source.pull-one), IterationEnd)
  114. ?? ($result := IterationEnd)
  115. !! ($stopped = 0)
  116. ),
  117. 'REDO', ($stopped = 0),
  118. 'LAST', nqp::stmts(
  119. ($!did-iterate = 1),
  120. ($result := IterationEnd)
  121. )
  122. ),
  123. :nohandler
  124. )
  125. }
  126. nqp::if(
  127. $!did-iterate && nqp::eqaddr($result,IterationEnd),
  128. &!block.fire_if_phasers('LAST')
  129. );
  130. $result
  131. }
  132. method push-all($target --> IterationEnd) {
  133. nqp::unless(
  134. $!did-init,
  135. nqp::stmts(
  136. ($!did-init = 1),
  137. nqp::if(
  138. &!block.has-phaser('FIRST'),
  139. nqp::p6setfirstflag(&!block)
  140. )
  141. )
  142. );
  143. my int $stopped;
  144. my int $done;
  145. my $pulled;
  146. my $value;
  147. until $done
  148. || nqp::eqaddr(($value := $!source.pull-one),IterationEnd) {
  149. nqp::stmts(
  150. ($stopped = 0),
  151. nqp::until(
  152. $stopped,
  153. nqp::stmts(
  154. ($stopped = 1),
  155. nqp::handle(
  156. nqp::stmts( # doesn't sink
  157. ($pulled := &!block($value)),
  158. ($!did-iterate = 1),
  159. nqp::if($!NEXT, &!block.fire_phasers('NEXT')),
  160. nqp::if(
  161. nqp::istype($pulled,Slip),
  162. self.slip-all($pulled,$target),
  163. $target.push($pulled)
  164. )
  165. ),
  166. 'LABELED', $!label,
  167. 'NEXT', nqp::stmts(
  168. ($!did-iterate = 1),
  169. nqp::if($!NEXT, &!block.fire_phasers('NEXT')),
  170. nqp::eqaddr(
  171. ($value := $!source.pull-one),
  172. IterationEnd
  173. )
  174. ?? ($done = 1)
  175. !! ($stopped = 0)),
  176. 'REDO', ($stopped = 0),
  177. 'LAST', ($done = $!did-iterate = 1)
  178. )
  179. ),
  180. :nohandler
  181. )
  182. )
  183. }
  184. nqp::if($!did-iterate,&!block.fire_if_phasers('LAST'))
  185. }
  186. method sink-all(--> IterationEnd) {
  187. nqp::unless(
  188. $!did-init,
  189. nqp::stmts(
  190. ($!did-init = 1),
  191. nqp::if(
  192. &!block.has-phaser('FIRST'),
  193. nqp::p6setfirstflag(&!block)
  194. )
  195. )
  196. );
  197. my int $stopped;
  198. my int $done;
  199. my $value;
  200. until $done
  201. || nqp::eqaddr(($value := $!source.pull-one()),IterationEnd) {
  202. nqp::stmts(
  203. ($stopped = 0),
  204. nqp::until(
  205. $stopped,
  206. nqp::stmts(
  207. ($stopped = 1),
  208. nqp::handle(
  209. nqp::stmts( # doesn't sink
  210. (&!block($value)),
  211. ($!did-iterate = 1),
  212. nqp::if($!NEXT, &!block.fire_phasers('NEXT')),
  213. ),
  214. 'LABELED', $!label,
  215. 'NEXT', nqp::stmts(
  216. ($!did-iterate = 1),
  217. nqp::if($!NEXT, &!block.fire_phasers('NEXT')),
  218. nqp::eqaddr(
  219. ($value := $!source.pull-one),
  220. IterationEnd
  221. )
  222. ?? ($done = 1)
  223. !! ($stopped = 0)),
  224. 'REDO', ($stopped = 0),
  225. 'LAST', ($done = $!did-iterate = 1)
  226. )
  227. ),
  228. :nohandler
  229. )
  230. )
  231. }
  232. nqp::if($!did-iterate,&!block.fire_if_phasers('LAST'))
  233. }
  234. }
  235. my class IterateOneNotSlippingWithoutPhasers does Iterator {
  236. has &!block;
  237. has $!source;
  238. has $!label;
  239. method new(&block,$source,$label) {
  240. my $iter := nqp::create(self);
  241. nqp::bindattr($iter, self, '&!block', &block);
  242. nqp::bindattr($iter, self, '$!source', $source);
  243. nqp::bindattr($iter, self, '$!label', nqp::decont($label));
  244. $iter
  245. }
  246. method is-lazy() { $!source.is-lazy }
  247. method pull-one() is raw {
  248. if nqp::eqaddr((my $pulled := $!source.pull-one),IterationEnd) {
  249. IterationEnd
  250. }
  251. else {
  252. my $result;
  253. my int $stopped;
  254. nqp::stmts(
  255. nqp::until(
  256. $stopped,
  257. nqp::stmts(
  258. ($stopped = 1),
  259. nqp::handle(
  260. ($result := &!block($pulled)),
  261. 'LABELED', $!label,
  262. 'NEXT', nqp::if(
  263. nqp::eqaddr(
  264. ($pulled := $!source.pull-one),
  265. IterationEnd
  266. ),
  267. ($result := IterationEnd),
  268. ($stopped = 0)
  269. ),
  270. 'REDO', ($stopped = 0),
  271. 'LAST', ($result := IterationEnd)
  272. ),
  273. ),
  274. :nohandler
  275. ),
  276. $result
  277. )
  278. }
  279. }
  280. method push-all($target --> IterationEnd) {
  281. my $pulled;
  282. my int $stopped;
  283. nqp::until(
  284. nqp::eqaddr(($pulled := $!source.pull-one),IterationEnd),
  285. nqp::stmts(
  286. ($stopped = 0),
  287. nqp::until(
  288. $stopped,
  289. nqp::stmts(
  290. ($stopped = 1),
  291. nqp::handle(
  292. $target.push(&!block($pulled)),
  293. 'LABELED', $!label,
  294. 'REDO', ($stopped = 0),
  295. 'LAST', return
  296. )
  297. ),
  298. :nohandler
  299. )
  300. )
  301. )
  302. }
  303. method sink-all(--> IterationEnd) {
  304. my $pulled;
  305. my int $stopped;
  306. nqp::until(
  307. nqp::eqaddr(($pulled := $!source.pull-one),IterationEnd),
  308. nqp::stmts(
  309. ($stopped = 0),
  310. nqp::until(
  311. $stopped,
  312. nqp::stmts(
  313. ($stopped = 1),
  314. nqp::handle(
  315. &!block($pulled),
  316. 'LABELED', $!label,
  317. 'REDO', ($stopped = 0),
  318. 'LAST', return
  319. )
  320. ),
  321. :nohandler
  322. )
  323. )
  324. )
  325. }
  326. }
  327. my class IterateOneWithoutPhasers does SlippyIterator {
  328. has &!block;
  329. has $!source;
  330. has $!label;
  331. method new(&block,$source,$label) {
  332. my $iter := nqp::create(self);
  333. nqp::bindattr($iter, self, '&!block', &block);
  334. nqp::bindattr($iter, self, '$!source', $source);
  335. nqp::bindattr($iter, self, '$!label', nqp::decont($label));
  336. $iter
  337. }
  338. method is-lazy() { $!source.is-lazy }
  339. method pull-one() is raw {
  340. my int $redo = 1;
  341. my $value;
  342. my $result;
  343. if $!slipping && nqp::not_i(nqp::eqaddr(
  344. ($result := self.slip-one),
  345. IterationEnd
  346. )) {
  347. # $result will be returned at the end
  348. }
  349. elsif nqp::eqaddr(
  350. ($value := $!source.pull-one),
  351. IterationEnd
  352. ) {
  353. $result := $value
  354. }
  355. else {
  356. nqp::while(
  357. $redo,
  358. nqp::stmts(
  359. $redo = 0,
  360. nqp::handle(
  361. nqp::if(
  362. nqp::istype(($result := &!block($value)),Slip),
  363. nqp::if(
  364. nqp::eqaddr(
  365. ($result := self.start-slip($result)), IterationEnd),
  366. nqp::if(
  367. nqp::not_i(nqp::eqaddr(
  368. ($value := $!source.pull-one),
  369. IterationEnd
  370. )),
  371. $redo = 1
  372. )
  373. )
  374. ),
  375. 'LABELED',
  376. $!label,
  377. 'NEXT',
  378. nqp::if(
  379. nqp::eqaddr(
  380. ($value := $!source.pull-one),IterationEnd
  381. ),
  382. ($result := IterationEnd),
  383. ($redo = 1)
  384. ),
  385. 'REDO',
  386. ($redo = 1),
  387. 'LAST',
  388. ($result := IterationEnd)
  389. ),
  390. ),
  391. :nohandler);
  392. }
  393. $result
  394. }
  395. method push-all($target --> IterationEnd) {
  396. # This extra scope serves no other purpose than to make this method JIT
  397. # and OSR faster.
  398. {
  399. my int $redo;
  400. my $value;
  401. my $result;
  402. nqp::until(
  403. nqp::eqaddr(($value := $!source.pull-one),IterationEnd),
  404. nqp::stmts(
  405. ($redo = 1),
  406. nqp::while(
  407. $redo,
  408. nqp::stmts(
  409. ($redo = 0),
  410. nqp::handle(
  411. nqp::if(
  412. nqp::istype(($result := &!block($value)),Slip),
  413. self.slip-all($result,$target),
  414. $target.push($result)
  415. ),
  416. 'LABELED', $!label,
  417. 'REDO', ($redo = 1),
  418. 'LAST', (return IterationEnd),
  419. )
  420. ),
  421. :nohandler
  422. )
  423. )
  424. )
  425. } # needed for faster JITting and OSRing
  426. }
  427. method sink-all(--> IterationEnd) {
  428. # This extra scope serves no other purpose than to make this method JIT
  429. # and OSR faster.
  430. {
  431. my int $redo;
  432. my $value;
  433. nqp::until(
  434. nqp::eqaddr(($value := $!source.pull-one()),IterationEnd),
  435. nqp::stmts(
  436. ($redo = 1),
  437. nqp::while(
  438. $redo,
  439. nqp::stmts(
  440. ($redo = 0),
  441. nqp::handle( # doesn't sink
  442. &!block($value),
  443. 'LABELED', $!label,
  444. 'NEXT', nqp::null, # need NEXT for next LABEL support
  445. 'REDO', ($redo = 1),
  446. 'LAST', (return IterationEnd)
  447. ),
  448. :nohandler
  449. )
  450. )
  451. )
  452. )
  453. } # needed for faster JITting and OSRing
  454. }
  455. }
  456. my class IterateTwoWithoutPhasers does SlippyIterator {
  457. has &!block;
  458. has $!source;
  459. has $!label;
  460. method new(&block,$source,$label) {
  461. my $iter := nqp::create(self);
  462. nqp::bindattr($iter, self, '&!block', &block);
  463. nqp::bindattr($iter, self, '$!source', $source);
  464. nqp::bindattr($iter, self, '$!label', nqp::decont($label));
  465. $iter
  466. }
  467. method is-lazy() { $!source.is-lazy }
  468. method pull-one() is raw {
  469. my int $redo = 1;
  470. my $value;
  471. my $value2;
  472. my $result;
  473. if $!slipping && nqp::not_i(nqp::eqaddr(
  474. ($result := self.slip-one),
  475. IterationEnd
  476. )) {
  477. # $result will be returned at the end
  478. }
  479. elsif nqp::eqaddr(
  480. ($value := $!source.pull-one),
  481. IterationEnd
  482. ) {
  483. $result := IterationEnd;
  484. }
  485. else {
  486. nqp::while(
  487. $redo,
  488. nqp::stmts(
  489. $redo = 0,
  490. nqp::handle(
  491. nqp::stmts(
  492. nqp::if(
  493. nqp::eqaddr(($value2 := $!source.pull-one),IterationEnd),
  494. nqp::if( # don't have 2 params
  495. nqp::istype(($result := &!block($value)),Slip),
  496. ($result := self.start-slip($result)) # don't care if empty
  497. ),
  498. nqp::if(
  499. nqp::istype(($result := &!block($value,$value2)),Slip),
  500. nqp::if(
  501. nqp::eqaddr(($result := self.start-slip($result)),IterationEnd),
  502. nqp::unless(
  503. nqp::eqaddr(($value := $!source.pull-one),IterationEnd),
  504. ($redo = 1)
  505. )
  506. )
  507. )
  508. )
  509. ),
  510. 'LABELED',
  511. $!label,
  512. 'NEXT',
  513. nqp::if(
  514. nqp::eqaddr(
  515. ($value := $!source.pull-one),IterationEnd
  516. ),
  517. ($result := IterationEnd),
  518. ($redo = 1)
  519. ),
  520. 'REDO',
  521. ($redo = 1),
  522. 'LAST',
  523. ($result := IterationEnd)
  524. ),
  525. ),
  526. :nohandler);
  527. }
  528. $result
  529. }
  530. method push-all($target --> IterationEnd) {
  531. # This extra scope serves no other purpose than to make this method JIT
  532. # and OSR faster.
  533. {
  534. my int $redo;
  535. my $value;
  536. my $value2;
  537. my $result;
  538. nqp::until(
  539. nqp::eqaddr(($value := $!source.pull-one),IterationEnd),
  540. nqp::stmts(
  541. ($redo = 1),
  542. nqp::while(
  543. $redo,
  544. nqp::stmts(
  545. ($redo = 0),
  546. nqp::handle(
  547. nqp::if(
  548. nqp::eqaddr(
  549. ($value2 := $!source.pull-one),
  550. IterationEnd
  551. ),
  552. nqp::stmts(
  553. ($result := &!block($value)),
  554. nqp::if(
  555. nqp::istype($result,Slip),
  556. self.slip-all($result,$target),
  557. $target.push($result)
  558. ),
  559. (return IterationEnd)
  560. ),
  561. nqp::if(
  562. nqp::istype(
  563. ($result := &!block($value,$value2)),
  564. Slip
  565. ),
  566. self.slip-all($result,$target),
  567. $target.push($result)
  568. )
  569. ),
  570. 'LABELED', $!label,
  571. 'REDO', ($redo = 1),
  572. 'LAST', (return IterationEnd)
  573. )
  574. ),
  575. :nohandler
  576. )
  577. )
  578. )
  579. } # needed for faster JITting and OSRing
  580. }
  581. method sink-all(--> IterationEnd) {
  582. # This extra scope serves no other purpose than to make this method JIT
  583. # and OSR faster.
  584. {
  585. my int $redo;
  586. my $value;
  587. my $value2;
  588. nqp::until(
  589. nqp::eqaddr(($value := $!source.pull-one()),IterationEnd),
  590. nqp::stmts(
  591. ($redo = 1),
  592. nqp::while(
  593. $redo,
  594. nqp::stmts(
  595. ($redo = 0),
  596. nqp::handle( # doesn't sink
  597. nqp::if(
  598. nqp::eqaddr(
  599. ($value2 := $!source.pull-one),
  600. IterationEnd
  601. ),
  602. nqp::stmts(
  603. (&!block($value)),
  604. (return IterationEnd)
  605. ),
  606. (&!block($value,$value2))
  607. ),
  608. 'LABELED', $!label,
  609. 'NEXT', nqp::null, # need NEXT for next LABEL support
  610. 'REDO', ($redo = 1),
  611. 'LAST', (return IterationEnd)
  612. )
  613. ),
  614. :nohandler
  615. )
  616. )
  617. )
  618. } # needed for faster JITting and OSRing
  619. }
  620. }
  621. my class IterateMoreWithPhasers does SlippyIterator {
  622. has &!block;
  623. has $!source;
  624. has $!count;
  625. has $!label;
  626. has $!value-buffer;
  627. has $!did-init;
  628. has $!did-iterate;
  629. has $!NEXT;
  630. has $!CAN_FIRE_PHASERS;
  631. method new(&block, $source, $count, $label) {
  632. my $iter := nqp::create(self);
  633. nqp::bindattr($iter, self, '&!block', &block);
  634. nqp::bindattr($iter, self, '$!source', $source);
  635. nqp::bindattr($iter, self, '$!count', $count);
  636. nqp::bindattr($iter, self, '$!label', nqp::decont($label));
  637. $iter
  638. }
  639. method is-lazy() { $!source.is-lazy }
  640. method pull-one() is raw {
  641. $!value-buffer.DEFINITE
  642. ?? nqp::setelems($!value-buffer, 0)
  643. !! ($!value-buffer := IterationBuffer.new);
  644. my int $redo = 1;
  645. my $result;
  646. if !$!did-init && nqp::can(&!block, 'fire_phasers') {
  647. $!did-init = 1;
  648. $!CAN_FIRE_PHASERS = 1;
  649. $!NEXT = &!block.has-phaser('NEXT');
  650. nqp::p6setfirstflag(&!block)
  651. if &!block.has-phaser('FIRST');
  652. }
  653. if $!slipping && !(($result := self.slip-one()) =:= IterationEnd) {
  654. # $result will be returned at the end
  655. }
  656. elsif $!source.push-exactly($!value-buffer, $!count) =:= IterationEnd
  657. && nqp::elems($!value-buffer) == 0 {
  658. $result := IterationEnd
  659. }
  660. else {
  661. nqp::while(
  662. $redo,
  663. nqp::stmts(
  664. $redo = 0,
  665. nqp::handle(
  666. nqp::stmts(
  667. ($result := nqp::p6invokeflat(&!block, $!value-buffer)),
  668. ($!did-iterate = 1),
  669. nqp::if(
  670. nqp::istype($result, Slip),
  671. nqp::stmts(
  672. ($result := self.start-slip($result)),
  673. nqp::if(
  674. nqp::eqaddr($result, IterationEnd),
  675. nqp::stmts(
  676. (nqp::setelems($!value-buffer, 0)),
  677. ($redo = 1
  678. unless nqp::eqaddr(
  679. $!source.push-exactly($!value-buffer, $!count),
  680. IterationEnd)
  681. && nqp::elems($!value-buffer) == 0)
  682. )
  683. )
  684. )
  685. ),
  686. nqp::if($!NEXT, &!block.fire_phasers('NEXT')),
  687. ),
  688. 'LABELED', $!label,
  689. 'NEXT', nqp::stmts(
  690. ($!did-iterate = 1),
  691. nqp::if($!NEXT, &!block.fire_phasers('NEXT')),
  692. (nqp::setelems($!value-buffer, 0)),
  693. nqp::eqaddr($!source.push-exactly($!value-buffer, $!count), IterationEnd)
  694. && nqp::elems($!value-buffer) == 0
  695. ?? ($result := IterationEnd)
  696. !! ($redo = 1)),
  697. 'REDO', $redo = 1,
  698. 'LAST', nqp::stmts(
  699. ($!did-iterate = 1),
  700. ($result := IterationEnd)
  701. )
  702. )
  703. ),
  704. :nohandler);
  705. }
  706. &!block.fire_if_phasers('LAST')
  707. if $!CAN_FIRE_PHASERS
  708. && $!did-iterate
  709. && nqp::eqaddr($result, IterationEnd);
  710. $result
  711. }
  712. }
  713. sub sequential-map(\source, &block, $label) {
  714. # We want map to be fast, so we go to some effort to build special
  715. # case iterators that can ignore various interesting cases.
  716. my $count = &block.count;
  717. Seq.new(
  718. nqp::istype(&block,Block) && &block.has-phasers
  719. ?? $count < 2 || $count === Inf
  720. ?? IterateOneWithPhasers.new(&block,source,$label)
  721. !! IterateMoreWithPhasers.new(&block,source,$count,$label)
  722. !! $count < 2 || $count === Inf
  723. ?? nqp::istype(Slip,&block.returns)
  724. ?? IterateOneWithoutPhasers.new(&block,source,$label)
  725. !! IterateOneNotSlippingWithoutPhasers.new(&block,source,$label)
  726. !! $count == 2
  727. ?? IterateTwoWithoutPhasers.new(&block,source,$label)
  728. !! IterateMoreWithPhasers.new(&block,source,$count,$label)
  729. )
  730. }
  731. proto method flatmap (|) is nodal { * }
  732. multi method flatmap(&block, :$label) {
  733. self.map(&block, :$label).flat
  734. }
  735. method !grep-k(Callable:D $test) {
  736. Seq.new(class :: does Iterator {
  737. has Mu $!iter;
  738. has Mu $!test;
  739. has int $!index;
  740. method !SET-SELF(\list,Mu \test) {
  741. $!iter = list.iterator;
  742. $!test := test;
  743. $!index = -1;
  744. self
  745. }
  746. method new(\list,Mu \test) { nqp::create(self)!SET-SELF(list,test) }
  747. method pull-one() is raw {
  748. $!index = $!index + 1
  749. until ($_ := $!iter.pull-one) =:= IterationEnd || $!test($_);
  750. $_ =:= IterationEnd
  751. ?? IterationEnd
  752. !! nqp::p6box_i($!index = $!index + 1)
  753. }
  754. method push-all($target) {
  755. until ($_ := $!iter.pull-one) =:= IterationEnd {
  756. $!index = $!index + 1;
  757. $target.push(nqp::p6box_i($!index)) if $!test($_);
  758. }
  759. IterationEnd
  760. }
  761. }.new(self, $test))
  762. }
  763. method !grep-kv(Callable:D $test) {
  764. Seq.new(class :: does Iterator {
  765. has Mu $!iter;
  766. has Mu $!test;
  767. has int $!index;
  768. has Mu $!value;
  769. method !SET-SELF(\list,Mu \test) {
  770. $!iter = list.iterator;
  771. $!test := test;
  772. $!index = -1;
  773. self
  774. }
  775. method new(\list,Mu \test) { nqp::create(self)!SET-SELF(list,test) }
  776. method pull-one() is raw {
  777. if $!value.DEFINITE {
  778. my \tmp = $!value;
  779. $!value := nqp::null;
  780. tmp
  781. }
  782. else {
  783. $!index = $!index + 1
  784. until ($_ := $!iter.pull-one) =:= IterationEnd
  785. || $!test($_);
  786. if $_ =:= IterationEnd {
  787. IterationEnd;
  788. }
  789. else {
  790. $!value := $_;
  791. nqp::p6box_i($!index = $!index + 1)
  792. }
  793. }
  794. }
  795. method push-all($target) {
  796. nqp::until(
  797. nqp::eqaddr(($_ := $!iter.pull-one),IterationEnd),
  798. nqp::stmts(
  799. $!index = nqp::add_i($!index,1);
  800. nqp::if(
  801. $!test($_),
  802. nqp::stmts( # doesn't sink
  803. $target.push(nqp::p6box_i($!index));
  804. $target.push($_);
  805. )
  806. )
  807. )
  808. );
  809. IterationEnd
  810. }
  811. }.new(self, $test))
  812. }
  813. method !grep-p(Callable:D $test) {
  814. Seq.new(class :: does Iterator {
  815. has Mu $!iter;
  816. has Mu $!test;
  817. has int $!index;
  818. method !SET-SELF(\list,Mu \test) {
  819. $!iter = list.iterator;
  820. $!test := test;
  821. $!index = -1;
  822. self
  823. }
  824. method new(\list,Mu \test) { nqp::create(self)!SET-SELF(list,test) }
  825. method pull-one() is raw {
  826. $!index = $!index + 1
  827. until ($_ := $!iter.pull-one) =:= IterationEnd || $!test($_);
  828. $_ =:= IterationEnd
  829. ?? IterationEnd
  830. !! Pair.new($!index = $!index + 1,$_)
  831. }
  832. method push-all($target) {
  833. until ($_ := $!iter.pull-one) =:= IterationEnd {
  834. $!index = $!index + 1;
  835. $target.push(Pair.new($!index,$_)) if $!test($_);
  836. }
  837. IterationEnd
  838. }
  839. }.new(self, $test))
  840. }
  841. role Grepper does Iterator {
  842. has Mu $!iter;
  843. has Mu $!test;
  844. method SET-SELF(\list,Mu \test) {
  845. $!iter = list.iterator;
  846. $!test := test;
  847. self
  848. }
  849. method new(\list,Mu \test) { nqp::create(self).SET-SELF(list,test) }
  850. method is-lazy() { $!iter.is-lazy }
  851. }
  852. method !grep-callable(Callable:D $test) {
  853. nqp::if(
  854. $test.count == 1,
  855. sequential-map(
  856. self.iterator,
  857. { nqp::if($test($_),$_,Empty) },
  858. Any)
  859. ,
  860. nqp::stmts(
  861. (my role CheatArity {
  862. has $!arity;
  863. has $!count;
  864. method set-cheat($new-arity, $new-count --> Nil) {
  865. $!arity = $new-arity;
  866. $!count = $new-count;
  867. }
  868. method arity(Code:D:) { $!arity }
  869. method count(Code:D:) { $!count }
  870. }),
  871. (my &tester = -> |c {
  872. #note "*cough* {c.perl} -> {$test(|c).perl}";
  873. next unless $test(|c);
  874. c.list
  875. } but CheatArity),
  876. &tester.set-cheat($test.arity, $test.count),
  877. self.map(&tester)
  878. )
  879. )
  880. }
  881. method !grep-accepts(Mu $test) {
  882. Seq.new(class :: does Grepper {
  883. method pull-one() is raw {
  884. nqp::until(
  885. nqp::eqaddr(($_ := $!iter.pull-one),IterationEnd)
  886. || $!test.ACCEPTS($_),
  887. nqp::null
  888. );
  889. $_
  890. }
  891. method push-all($target) {
  892. nqp::until(
  893. nqp::eqaddr(($_ := $!iter.pull-one),IterationEnd),
  894. nqp::if( # doesn't sink
  895. $!test.ACCEPTS($_),
  896. $target.push($_)
  897. )
  898. );
  899. IterationEnd
  900. }
  901. }.new(self, $test))
  902. }
  903. method !first-result(\index,\value,$what,%a) is raw {
  904. nqp::stmts(
  905. (my $storage := nqp::getattr(%a,Map,'$!storage')),
  906. nqp::if(
  907. nqp::elems($storage), # some adverb
  908. nqp::if(
  909. nqp::iseq_i(nqp::elems($storage),1), # one adverb
  910. nqp::if(
  911. nqp::atkey($storage,"k"), # :k
  912. nqp::p6box_i(index),
  913. nqp::if(
  914. nqp::atkey($storage,"p"), # :p
  915. Pair.new(index,value),
  916. nqp::if(
  917. nqp::atkey($storage,"v"), # :v
  918. value,
  919. nqp::if(
  920. nqp::atkey($storage,"kv"), # :kv
  921. (index,value),
  922. nqp::stmts( # no truthy or different
  923. (my str $key =
  924. nqp::iterkey_s(nqp::shift(nqp::iterator($storage)))),
  925. nqp::if(
  926. (nqp::iseq_s($key,"k") # :!k || :!p || :!kv
  927. || nqp::iseq_s($key,"p")
  928. || nqp::iseq_s($key,"kv")),
  929. value,
  930. nqp::if(
  931. nqp::iseq_s($key,"v"), # :!v
  932. Failure.new("Specified a negated :v adverb"),
  933. Failure.new(X::Adverb.new( # :foo ??
  934. :$what,
  935. :source(try { self.VAR.name } // self.WHAT.perl),
  936. :unexpected(%a.keys)))
  937. )
  938. )
  939. )
  940. )
  941. )
  942. )
  943. ),
  944. Failure.new(X::Adverb.new( # multiple adverbs ??
  945. :$what,
  946. :source(try { self.VAR.name } // self.WHAT.perl),
  947. :nogo(%a.keys.grep: /k|v|p/)
  948. :unexpected(%a.keys.grep: { !.match(/k|v|p/) } )))
  949. ),
  950. value # no adverb
  951. )
  952. )
  953. }
  954. proto method grep(|) is nodal { * }
  955. multi method grep(Bool:D $t) {
  956. X::Match::Bool.new( type => '.grep').throw
  957. }
  958. multi method grep(Mu $t) {
  959. my $storage := nqp::getattr(%_,Map,'$!storage');
  960. if nqp::iseq_i(nqp::elems($storage),0) {
  961. nqp::istype($t,Regex:D)
  962. ?? self!grep-accepts: $t
  963. !! nqp::istype($t,Callable:D)
  964. ?? self!grep-callable: $t
  965. !! self!grep-accepts: $t
  966. }
  967. elsif nqp::iseq_i(nqp::elems($storage),1) {
  968. if nqp::atkey($storage,"k") {
  969. nqp::istype($t,Regex:D)
  970. ?? self!grep-k: { $t.ACCEPTS($_) }
  971. !! nqp::istype($t,Callable:D)
  972. ?? self!grep-k: $t
  973. !! self!grep-k: { $t.ACCEPTS($_) }
  974. }
  975. elsif nqp::atkey($storage,"kv") {
  976. nqp::istype($t,Regex:D)
  977. ?? self!grep-kv: { $t.ACCEPTS($_) }
  978. !! nqp::istype($t,Callable:D)
  979. ?? self!grep-kv: $t
  980. !! self!grep-kv: { $t.ACCEPTS($_) }
  981. }
  982. elsif nqp::atkey($storage,"p") {
  983. nqp::istype($t,Regex:D)
  984. ?? self!grep-p: { $t.ACCEPTS($_) }
  985. !! nqp::istype($t,Callable:D)
  986. ?? self!grep-p: $t
  987. !! self!grep-p: { $t.ACCEPTS($_) }
  988. }
  989. elsif nqp::atkey($storage,"v") {
  990. nqp::istype($t,Regex:D)
  991. ?? self!grep-accepts: $t
  992. !! nqp::istype($t,Callable:D)
  993. ?? self!grep-callable: $t
  994. !! self!grep-accepts: $t
  995. }
  996. else {
  997. my str $key =
  998. nqp::iterkey_s(nqp::shift(nqp::iterator($storage)));
  999. if nqp::iseq_s($key,"k") || nqp::iseq_s($key,"kv") || nqp::iseq_s($key,"p") {
  1000. nqp::istype($t,Regex:D)
  1001. ?? self!grep-accepts: $t
  1002. !! nqp::istype($t,Callable:D)
  1003. ?? self!grep-callable: $t
  1004. !! self!grep-accepts: $t
  1005. }
  1006. else {
  1007. nqp::iseq_s($key,"k")
  1008. ?? die "Specified a negated :v adverb"
  1009. !! X::Adverb.new(
  1010. :what<grep>,
  1011. :source(try { self.VAR.name } // self.WHAT.perl),
  1012. :unexpected($key)
  1013. ).throw
  1014. }
  1015. }
  1016. }
  1017. else {
  1018. X::Adverb.new(
  1019. :what<grep>,
  1020. :source(try { self.VAR.name } // self.WHAT.perl),
  1021. :nogo(%_.keys.grep: /k|v|kv|p/)
  1022. :unexpected(%_.keys.grep: { !.match(/k|v|kv|p/) } )
  1023. ).throw
  1024. }
  1025. }
  1026. proto method first(|) is nodal { * }
  1027. multi method first(Bool:D $t) {
  1028. Failure.new(X::Match::Bool.new( type => '.first' ))
  1029. }
  1030. # need to handle Regex differently, since it is also Callable
  1031. multi method first(Regex:D $test, :$end, *%a) is raw {
  1032. $end
  1033. ?? self!first-accepts-end($test,%a)
  1034. !! self!first-accepts($test,%a)
  1035. }
  1036. multi method first(Callable:D $test, :$end, *%a is copy) is raw {
  1037. if $end {
  1038. nqp::stmts(
  1039. (my $elems = self.elems),
  1040. nqp::if(
  1041. ($elems && nqp::not_i($elems == Inf)),
  1042. nqp::stmts(
  1043. (my int $index = $elems),
  1044. nqp::while(
  1045. nqp::isge_i(($index = nqp::sub_i($index,1)),0),
  1046. nqp::if(
  1047. $test(self.AT-POS($index)),
  1048. return self!first-result(
  1049. $index,self.AT-POS($index),'first :end',%a)
  1050. )
  1051. ),
  1052. Nil
  1053. ),
  1054. Nil
  1055. )
  1056. )
  1057. }
  1058. else {
  1059. nqp::stmts(
  1060. (my $iter := self.iterator),
  1061. (my int $index),
  1062. nqp::until(
  1063. (nqp::eqaddr(($_ := $iter.pull-one),IterationEnd)
  1064. || $test($_)),
  1065. ($index = nqp::add_i($index,1))
  1066. ),
  1067. nqp::if(
  1068. nqp::eqaddr($_,IterationEnd),
  1069. Nil,
  1070. self!first-result($index,$_,'first',%a)
  1071. )
  1072. )
  1073. }
  1074. }
  1075. multi method first(Mu $test = True, :$end, *%a) is raw {
  1076. $end
  1077. ?? self!first-accepts-end($test,%a)
  1078. !! self!first-accepts($test,%a)
  1079. }
  1080. method !first-accepts(Mu $test,%a) is raw {
  1081. nqp::stmts(
  1082. (my $iter := self.iterator),
  1083. (my int $index),
  1084. nqp::until(
  1085. (nqp::eqaddr(($_ := $iter.pull-one),IterationEnd)
  1086. || $test.ACCEPTS($_)),
  1087. ($index = nqp::add_i($index,1))
  1088. ),
  1089. nqp::if(
  1090. nqp::eqaddr($_,IterationEnd),
  1091. Nil,
  1092. self!first-result($index,$_,'first',%a)
  1093. )
  1094. )
  1095. }
  1096. method !first-accepts-end(Mu $test,%a) is raw {
  1097. nqp::stmts(
  1098. (my $elems = self.elems),
  1099. nqp::if(
  1100. ($elems && nqp::not_i($elems == Inf)),
  1101. nqp::stmts(
  1102. (my int $index = $elems),
  1103. nqp::while(
  1104. nqp::isge_i(($index = nqp::sub_i($index,1)),0),
  1105. nqp::if(
  1106. $test.ACCEPTS(self.AT-POS($index)),
  1107. return self!first-result(
  1108. $index,self.AT-POS($index),'first :end',%a)
  1109. )
  1110. ),
  1111. Nil
  1112. ),
  1113. Nil
  1114. )
  1115. )
  1116. }
  1117. method !iterator-and-first($action,\first) is raw {
  1118. nqp::if(
  1119. self.is-lazy,
  1120. X::Cannot::Lazy.new(:$action).throw,
  1121. nqp::stmts(
  1122. (my $iterator := self.iterator),
  1123. nqp::until(
  1124. nqp::eqaddr((my $pulled := $iterator.pull-one),IterationEnd),
  1125. nqp::if(
  1126. nqp::isconcrete($pulled),
  1127. nqp::stmts(
  1128. (first = $pulled),
  1129. (return $iterator)
  1130. )
  1131. )
  1132. ),
  1133. Mu
  1134. )
  1135. )
  1136. }
  1137. proto method min (|) is nodal { * }
  1138. multi method min() {
  1139. nqp::stmts(
  1140. nqp::if(
  1141. (my $iter := self!iterator-and-first(".min",my $min)),
  1142. nqp::until(
  1143. nqp::eqaddr((my $pulled := $iter.pull-one),IterationEnd),
  1144. nqp::if(
  1145. (nqp::isconcrete($pulled) && $pulled cmp $min < 0),
  1146. $min = $pulled
  1147. )
  1148. )
  1149. ),
  1150. nqp::if(nqp::defined($min),$min,Inf)
  1151. )
  1152. }
  1153. multi method min(&by) {
  1154. nqp::stmts(
  1155. (my $cmp := nqp::if(
  1156. nqp::iseq_i(&by.arity,2),&by,{ &by($^a) cmp &by($^b) })),
  1157. nqp::if(
  1158. (my $iter := self!iterator-and-first(".min",my $min)),
  1159. nqp::until(
  1160. nqp::eqaddr((my $pulled := $iter.pull-one),IterationEnd),
  1161. nqp::if(
  1162. (nqp::isconcrete($pulled) && $cmp($pulled,$min) < 0),
  1163. $min = $pulled
  1164. )
  1165. )
  1166. ),
  1167. nqp::if(nqp::defined($min),$min,Inf)
  1168. )
  1169. }
  1170. proto method max (|) is nodal { * }
  1171. multi method max() {
  1172. nqp::stmts(
  1173. nqp::if(
  1174. (my $iter := self!iterator-and-first(".max",my $max)),
  1175. nqp::until(
  1176. nqp::eqaddr((my $pulled := $iter.pull-one),IterationEnd),
  1177. nqp::if(
  1178. (nqp::isconcrete($pulled) && $pulled cmp $max > 0),
  1179. $max = $pulled
  1180. )
  1181. )
  1182. ),
  1183. nqp::if(nqp::defined($max),$max,-Inf)
  1184. )
  1185. }
  1186. multi method max(&by) {
  1187. nqp::stmts(
  1188. (my $cmp := nqp::if(
  1189. nqp::iseq_i(&by.arity,2),&by,{ &by($^a) cmp &by($^b) })),
  1190. nqp::if(
  1191. (my $iter := self!iterator-and-first(".max",my $max)),
  1192. nqp::until(
  1193. nqp::eqaddr((my $pulled := $iter.pull-one),IterationEnd),
  1194. nqp::if(
  1195. (nqp::isconcrete($pulled) && $cmp($pulled,$max) > 0),
  1196. $max = $pulled
  1197. )
  1198. )
  1199. ),
  1200. nqp::if(nqp::defined($max),$max,-Inf)
  1201. )
  1202. }
  1203. method !minmax-range-init(\value,\mi,\exmi,\ma,\exma --> Nil) {
  1204. mi = value.min;
  1205. exmi = value.excludes-min;
  1206. ma = value.max;
  1207. exma = value.excludes-max;
  1208. }
  1209. method !minmax-range-check(\value,\mi,\exmi,\ma,\exma --> Nil) {
  1210. nqp::stmts(
  1211. nqp::if(
  1212. ((value.min cmp mi) < 0),
  1213. nqp::stmts(
  1214. (mi = value.min),
  1215. (exmi = value.excludes-min)
  1216. )
  1217. ),
  1218. nqp::if(
  1219. ((value.max cmp ma) > 0),
  1220. nqp::stmts(
  1221. (ma = value.max),
  1222. (exma = value.excludes-max)
  1223. )
  1224. )
  1225. )
  1226. }
  1227. method !cmp-minmax-range-check(\value,$cmp,\mi,\exmi,\ma,\exma --> Nil) {
  1228. nqp::stmts( # $cmp sigillless confuses the optimizer
  1229. nqp::if(
  1230. ($cmp(value.min,mi) < 0),
  1231. nqp::stmts(
  1232. (mi = value.min),
  1233. (exmi = value.excludes-min)
  1234. )
  1235. ),
  1236. nqp::if(
  1237. ($cmp(value.max,ma) > 0),
  1238. nqp::stmts(
  1239. (ma = value.max),
  1240. (exma = value.excludes-max)
  1241. )
  1242. )
  1243. )
  1244. }
  1245. proto method minmax (|) is nodal { * }
  1246. multi method minmax() {
  1247. nqp::stmts(
  1248. nqp::if(
  1249. (my $iter := self!iterator-and-first(".minmax",my $pulled)),
  1250. nqp::stmts(
  1251. nqp::if(
  1252. nqp::istype($pulled,Range),
  1253. self!minmax-range-init($pulled,
  1254. my $min,my int $excludes-min,my $max,my int $excludes-max),
  1255. nqp::if(
  1256. nqp::istype($pulled,Positional),
  1257. self!minmax-range-init($pulled.minmax, # recurse for min/max
  1258. $min,$excludes-min,$max,$excludes-max),
  1259. ($min = $max = $pulled)
  1260. )
  1261. ),
  1262. nqp::until(
  1263. nqp::eqaddr(($pulled := $iter.pull-one),IterationEnd),
  1264. nqp::if(
  1265. nqp::isconcrete($pulled),
  1266. nqp::if(
  1267. nqp::istype($pulled,Range),
  1268. self!minmax-range-check($pulled,
  1269. $min,$excludes-min,$max,$excludes-max),
  1270. nqp::if(
  1271. nqp::istype($pulled,Positional),
  1272. self!minmax-range-check($pulled.minmax,
  1273. $min,$excludes-min,$max,$excludes-max),
  1274. nqp::if(
  1275. (($pulled cmp $min) < 0),
  1276. ($min = $pulled),
  1277. nqp::if(
  1278. (($pulled cmp $max) > 0),
  1279. ($max = $pulled)
  1280. )
  1281. )
  1282. )
  1283. )
  1284. )
  1285. )
  1286. )
  1287. ),
  1288. nqp::if(
  1289. nqp::defined($min),
  1290. Range.new($min,$max,:$excludes-min,:$excludes-max),
  1291. Range.new(Inf,-Inf)
  1292. )
  1293. )
  1294. }
  1295. multi method minmax(&by) {
  1296. nqp::stmts(
  1297. nqp::if(
  1298. (my $iter := self!iterator-and-first(".minmax",my $pulled)),
  1299. nqp::stmts(
  1300. (my $cmp = nqp::if(
  1301. nqp::iseq_i(&by.arity,2),&by,{ &by($^a) cmp &by($^b) })
  1302. ),
  1303. nqp::if(
  1304. nqp::istype($pulled,Range),
  1305. self!minmax-range-init($pulled,
  1306. my $min,my int $excludes-min,my $max,my int $excludes-max),
  1307. nqp::if(
  1308. nqp::istype($pulled,Positional),
  1309. self!minmax-range-init($pulled.minmax(&by), # recurse min/max
  1310. $min,$excludes-min,$max,$excludes-max),
  1311. ($min = $max = $pulled)
  1312. )
  1313. ),
  1314. nqp::until(
  1315. nqp::eqaddr(($pulled := $iter.pull-one),IterationEnd),
  1316. nqp::if(
  1317. nqp::isconcrete($pulled),
  1318. nqp::if(
  1319. nqp::istype($pulled,Range),
  1320. self!cmp-minmax-range-check($pulled,
  1321. $cmp,$min,$excludes-min,$max,$excludes-max),
  1322. nqp::if(
  1323. nqp::istype($pulled,Positional),
  1324. self!cmp-minmax-range-check($pulled.minmax(&by),
  1325. $cmp,$min,$excludes-min,$max,$excludes-max),
  1326. nqp::if(
  1327. ($cmp($pulled,$min) < 0),
  1328. ($min = $pulled),
  1329. nqp::if(
  1330. ($cmp($pulled,$max) > 0),
  1331. ($max = $pulled)
  1332. )
  1333. )
  1334. )
  1335. )
  1336. )
  1337. )
  1338. )
  1339. ),
  1340. nqp::if(
  1341. nqp::defined($min),
  1342. Range.new($min,$max,:$excludes-min,:$excludes-max),
  1343. Range.new(Inf,-Inf)
  1344. )
  1345. )
  1346. }
  1347. proto method sort(|) is nodal { * }
  1348. multi method sort() {
  1349. nqp::if(
  1350. nqp::eqaddr(
  1351. self.iterator.push-until-lazy(my $list := IterationBuffer.new),
  1352. IterationEnd
  1353. ),
  1354. Seq.new(
  1355. Rakudo::Iterator.ReifiedList(
  1356. Rakudo::Sorting.MERGESORT-REIFIED-LIST(
  1357. nqp::p6bindattrinvres(nqp::create(List),List,'$!reified',$list)
  1358. )
  1359. )
  1360. ),
  1361. X::Cannot::Lazy.new(:action<sort>).throw
  1362. )
  1363. }
  1364. multi method sort(&by) {
  1365. nqp::stmts(
  1366. nqp::unless(
  1367. nqp::eqaddr(
  1368. self.iterator.push-until-lazy(my $list := IterationBuffer.new),
  1369. IterationEnd
  1370. ),
  1371. X::Cannot::Lazy.new(:action<sort>).throw
  1372. ),
  1373. Seq.new(
  1374. Rakudo::Iterator.ReifiedList(
  1375. nqp::if(
  1376. nqp::eqaddr(&by,&infix:<cmp>),
  1377. Rakudo::Sorting.MERGESORT-REIFIED-LIST(
  1378. nqp::p6bindattrinvres(
  1379. nqp::create(List),List,'$!reified',$list)
  1380. ),
  1381. nqp::if(
  1382. &by.count < 2,
  1383. Rakudo::Sorting.MERGESORT-REIFIED-LIST-AS(
  1384. nqp::p6bindattrinvres(
  1385. nqp::create(List),List,'$!reified',$list),
  1386. &by
  1387. ),
  1388. Rakudo::Sorting.MERGESORT-REIFIED-LIST-WITH(
  1389. nqp::p6bindattrinvres(
  1390. nqp::create(List),List,'$!reified',$list),
  1391. &by
  1392. )
  1393. )
  1394. )
  1395. )
  1396. )
  1397. )
  1398. }
  1399. method collate {
  1400. self.sort(&[coll]);
  1401. }
  1402. sub find-reducer-for-op(&op) {
  1403. nqp::if(
  1404. nqp::iseq_s(&op.prec("prec"),"f="),
  1405. &METAOP_REDUCE_LISTINFIX,
  1406. nqp::if(
  1407. nqp::iseq_i(nqp::chars(my str $assoc = &op.prec("assoc")),0),
  1408. &METAOP_REDUCE_LEFT,
  1409. ::(nqp::concat('&METAOP_REDUCE_',nqp::uc($assoc)))
  1410. )
  1411. )
  1412. }
  1413. proto method reduce(|) { * }
  1414. multi method reduce(&with) is nodal {
  1415. return unless self.DEFINITE;
  1416. my $reducer := find-reducer-for-op(&with);
  1417. $reducer(&with)(self) if $reducer;
  1418. }
  1419. proto method produce(|) { * }
  1420. multi method produce(&with) is nodal {
  1421. return unless self.DEFINITE;
  1422. my $reducer := find-reducer-for-op(&with);
  1423. $reducer(&with,1)(self) if $reducer;
  1424. }
  1425. proto method unique(|) is nodal {*}
  1426. multi method unique() {
  1427. Seq.new(class :: does Iterator {
  1428. has $!iter;
  1429. has $!seen;
  1430. method !SET-SELF(\list) {
  1431. nqp::stmts(
  1432. ($!iter := list.iterator),
  1433. ($!seen := nqp::hash),
  1434. self
  1435. )
  1436. }
  1437. method new(\list) { nqp::create(self)!SET-SELF(list) }
  1438. method pull-one() {
  1439. nqp::stmts(
  1440. nqp::until(
  1441. nqp::eqaddr((my $pulled := $!iter.pull-one),IterationEnd)
  1442. || (nqp::not_i(nqp::existskey(
  1443. $!seen,
  1444. (my $needle := $pulled.WHICH)
  1445. )) && nqp::bindkey($!seen,$needle,1)),
  1446. nqp::null
  1447. ),
  1448. $pulled
  1449. )
  1450. }
  1451. method push-all($target --> IterationEnd) {
  1452. nqp::until(
  1453. nqp::eqaddr((my $pulled := $!iter.pull-one),IterationEnd),
  1454. nqp::unless(
  1455. nqp::existskey($!seen,(my $needle := $pulled.WHICH)),
  1456. nqp::stmts(
  1457. nqp::bindkey($!seen,$needle,1),
  1458. $target.push($pulled)
  1459. )
  1460. )
  1461. )
  1462. }
  1463. method is-lazy() { $!iter.is-lazy }
  1464. method sink-all(--> IterationEnd) { $!iter.sink-all }
  1465. }.new(self))
  1466. }
  1467. multi method unique( :&as!, :&with! ) {
  1468. my @seen; # should be Mu, but doesn't work in settings :-(
  1469. my Mu $target;
  1470. gather self.map: {
  1471. $target = &as($_);
  1472. if first( { with($target,$_) }, @seen ) =:= Nil {
  1473. @seen.push($target);
  1474. take $_;
  1475. }
  1476. };
  1477. }
  1478. multi method unique( :&as! ) {
  1479. Seq.new(class :: does Iterator {
  1480. has Mu $!iter;
  1481. has &!as;
  1482. has $!seen;
  1483. method !SET-SELF(\list, &!as) {
  1484. $!iter = list.iterator;
  1485. $!seen := nqp::hash();
  1486. self
  1487. }
  1488. method new(\list, &as) { nqp::create(self)!SET-SELF(list, &as) }
  1489. method pull-one() {
  1490. my Mu $value;
  1491. my str $needle;
  1492. nqp::until(
  1493. nqp::eqaddr(($value := $!iter.pull-one),IterationEnd),
  1494. nqp::unless(
  1495. nqp::existskey($!seen,$needle = nqp::unbox_s(&!as($value).WHICH)),
  1496. nqp::stmts(
  1497. nqp::bindkey($!seen, $needle, 1),
  1498. return $value
  1499. )
  1500. )
  1501. );
  1502. IterationEnd
  1503. }
  1504. method push-all($target) {
  1505. my Mu $value;
  1506. my str $needle;
  1507. nqp::until(
  1508. nqp::eqaddr(($value := $!iter.pull-one),IterationEnd),
  1509. nqp::unless(
  1510. nqp::existskey($!seen,$needle = nqp::unbox_s(&!as($value).WHICH)),
  1511. nqp::stmts( # doesn't sink
  1512. nqp::bindkey($!seen, $needle, 1),
  1513. $target.push($value)
  1514. )
  1515. )
  1516. );
  1517. IterationEnd
  1518. }
  1519. }.new(self, &as))
  1520. }
  1521. multi method unique( :&with! ) {
  1522. nextwith() if &with === &[===]; # use optimized version
  1523. my @seen; # should be Mu, but doesn't work in settings :-(
  1524. my Mu $target;
  1525. gather self.map: {
  1526. $target := $_;
  1527. if first( { with($target,$_) }, @seen ) =:= Nil {
  1528. @seen.push($target);
  1529. take $_;
  1530. }
  1531. }
  1532. }
  1533. proto method repeated(|) is nodal {*}
  1534. multi method repeated() {
  1535. Seq.new(class :: does Iterator {
  1536. has Mu $!iter;
  1537. has $!seen;
  1538. method !SET-SELF(\list) {
  1539. $!iter = list.iterator;
  1540. $!seen := nqp::hash();
  1541. self
  1542. }
  1543. method new(\list) { nqp::create(self)!SET-SELF(list) }
  1544. method pull-one() {
  1545. my Mu $value;
  1546. my str $needle;
  1547. nqp::until(
  1548. nqp::eqaddr(($value := $!iter.pull-one),IterationEnd),
  1549. nqp::existskey($!seen,$needle = nqp::unbox_s($value.WHICH))
  1550. ?? return $value
  1551. !! nqp::bindkey($!seen, $needle, 1)
  1552. );
  1553. IterationEnd
  1554. }
  1555. method push-all($target) {
  1556. my Mu $value;
  1557. my str $needle;
  1558. nqp::until( # doesn't sink
  1559. nqp::eqaddr(($value := $!iter.pull-one),IterationEnd),
  1560. nqp::existskey($!seen,$needle = nqp::unbox_s($value.WHICH))
  1561. ?? $target.push($value)
  1562. !! nqp::bindkey($!seen, $needle, 1)
  1563. );
  1564. IterationEnd
  1565. }
  1566. method is-lazy() { $!iter.is-lazy }
  1567. }.new(self))
  1568. }
  1569. multi method repeated( :&as!, :&with! ) {
  1570. my @seen; # should be Mu, but doesn't work in settings :-(
  1571. my Mu $target;
  1572. gather self.map: {
  1573. $target = &as($_);
  1574. first( { with($target,$_) }, @seen ) =:= Nil
  1575. ?? @seen.push($target)
  1576. !! take $_;
  1577. };
  1578. }
  1579. multi method repeated( :&as! ) {
  1580. Seq.new(class :: does Iterator {
  1581. has Mu $!iter;
  1582. has &!as;
  1583. has $!seen;
  1584. method !SET-SELF(\list, &!as) {
  1585. $!iter = list.iterator;
  1586. $!seen := nqp::hash();
  1587. self
  1588. }
  1589. method new(\list, &as) { nqp::create(self)!SET-SELF(list, &as) }
  1590. method pull-one() {
  1591. my Mu $value;
  1592. my str $needle;
  1593. nqp::until(
  1594. nqp::eqaddr(($value := $!iter.pull-one),IterationEnd),
  1595. nqp::existskey($!seen,$needle = nqp::unbox_s(&!as($value).WHICH))
  1596. ?? return $value
  1597. !! nqp::bindkey($!seen, $needle, 1)
  1598. );
  1599. IterationEnd
  1600. }
  1601. method push-all($target) {
  1602. my Mu $value;
  1603. my str $needle;
  1604. nqp::until( # doesn't sink
  1605. nqp::eqaddr(($value := $!iter.pull-one),IterationEnd),
  1606. nqp::existskey($!seen,$needle = nqp::unbox_s(&!as($value).WHICH))
  1607. ?? $target.push($value)
  1608. !! nqp::bindkey($!seen, $needle, 1)
  1609. );
  1610. IterationEnd
  1611. }
  1612. method is-lazy() { $!iter.is-lazy }
  1613. }.new(self, &as))
  1614. }
  1615. multi method repeated( :&with! ) {
  1616. nextwith() if &with === &[===]; # use optimized version
  1617. my @seen; # should be Mu, but doesn't work in settings :-(
  1618. my Mu $target;
  1619. gather self.map: {
  1620. $target := $_;
  1621. first( { with($target,$_) }, @seen ) =:= Nil
  1622. ?? @seen.push($target)
  1623. !! take $_;
  1624. }
  1625. }
  1626. proto method squish(|) is nodal {*}
  1627. multi method squish( :&as!, :&with = &[===] ) {
  1628. Seq.new(class :: does Iterator {
  1629. has Mu $!iter;
  1630. has &!as;
  1631. has &!with;
  1632. has $!last_as;
  1633. has int $!first;
  1634. method !SET-SELF(\list, &!as, &!with) {
  1635. $!iter = list.iterator;
  1636. $!first = 1;
  1637. self
  1638. }
  1639. method new(\list, &as, &with) {
  1640. nqp::create(self)!SET-SELF(list, &as, &with)
  1641. }
  1642. method pull-one() {
  1643. my Mu $value := $!iter.pull-one;
  1644. unless nqp::eqaddr($value,IterationEnd) {
  1645. my $which := &!as($value);
  1646. if $!first {
  1647. $!first = 0;
  1648. }
  1649. else {
  1650. until !with($!last_as, $which) or ($value := $!iter.pull-one) =:= IterationEnd {
  1651. $!last_as = $which;
  1652. $which := &!as($value);
  1653. }
  1654. }
  1655. $!last_as = $which;
  1656. }
  1657. $value;
  1658. }
  1659. method push-all($target) {
  1660. my Mu $value := $!iter.pull-one;
  1661. unless nqp::eqaddr($value,IterationEnd) {
  1662. my $which;
  1663. my $last_as := $!last_as;
  1664. nqp::if(
  1665. $!first,
  1666. nqp::stmts( # doesn't sink
  1667. ($target.push($value)),
  1668. ($which := &!as($value)),
  1669. ($last_as := $which),
  1670. ($value := $!iter.pull-one)
  1671. )
  1672. );
  1673. nqp::until(
  1674. nqp::eqaddr($value,IterationEnd),
  1675. nqp::stmts(
  1676. nqp::unless( # doesn't sink
  1677. with($last_as,$which := &!as($value)),
  1678. $target.push($value)
  1679. ),
  1680. ($last_as := $which),
  1681. ($value := $!iter.pull-one)
  1682. )
  1683. );
  1684. }
  1685. IterationEnd
  1686. }
  1687. method is-lazy() { $!iter.is-lazy }
  1688. }.new(self, &as, &with))
  1689. }
  1690. multi method squish( :&with = &[===] ) {
  1691. Seq.new(class :: does Iterator {
  1692. has Mu $!iter;
  1693. has &!with;
  1694. has Mu $!last;
  1695. has int $!first;
  1696. method !SET-SELF(\list, &!with) {
  1697. $!iter = list.iterator;
  1698. $!first = 1;
  1699. self
  1700. }
  1701. method new(\list, &with) { nqp::create(self)!SET-SELF(list, &with) }
  1702. method pull-one() {
  1703. my Mu $value := $!iter.pull-one;
  1704. unless nqp::eqaddr($value,IterationEnd) {
  1705. if $!first {
  1706. $!first = 0;
  1707. }
  1708. else {
  1709. my $ov = $value;
  1710. until !with($!last, $value)
  1711. or ($value := $!iter.pull-one) =:= IterationEnd {
  1712. $!last = $ov;
  1713. $ov = $value;
  1714. }
  1715. }
  1716. $!last = $value
  1717. }
  1718. $value;
  1719. }
  1720. method push-all($target) {
  1721. my Mu $value := $!iter.pull-one;
  1722. unless nqp::eqaddr($value,IterationEnd) {
  1723. my $last_val = $!last;
  1724. nqp::if(
  1725. $!first,
  1726. nqp::stmts( # doesn't sink
  1727. ($target.push($value)),
  1728. ($last_val := $value),
  1729. ($value := $!iter.pull-one)
  1730. )
  1731. );
  1732. nqp::until(
  1733. nqp::eqaddr($value,IterationEnd),
  1734. nqp::stmts(
  1735. nqp::unless( # doesn't sink
  1736. with($last_val, $value),
  1737. $target.push($value)
  1738. ),
  1739. ($last_val := $value),
  1740. ($value := $!iter.pull-one)
  1741. )
  1742. );
  1743. }
  1744. IterationEnd
  1745. }
  1746. method is-lazy() { $!iter.is-lazy }
  1747. }.new(self, &with))
  1748. }
  1749. proto method pairup(|) is nodal { * }
  1750. multi method pairup(Any:U:) { () }
  1751. multi method pairup(Any:D:) {
  1752. my \iter = nqp::istype(self, Iterable)
  1753. ?? self.iterator
  1754. !! self.list.iterator;
  1755. gather loop {
  1756. my $it := iter.pull-one;
  1757. if nqp::istype($it, Pair) {
  1758. take $it.key => $it.value
  1759. }
  1760. elsif nqp::istype($it, Map) and !nqp::iscont($it) {
  1761. take Slip.new(|$it.pairs)
  1762. }
  1763. elsif $it =:= IterationEnd {
  1764. last
  1765. }
  1766. else {
  1767. my $it-value := iter.pull-one;
  1768. if $it-value =:= IterationEnd {
  1769. X::Pairup::OddNumber.new.throw;
  1770. }
  1771. take $it => $it-value;
  1772. }
  1773. }
  1774. }
  1775. proto method head(|) { * }
  1776. multi method head(Any:D:) is raw {
  1777. nqp::if(
  1778. nqp::eqaddr((my $pulled := self.iterator.pull-one),IterationEnd),
  1779. Nil,
  1780. $pulled
  1781. )
  1782. }
  1783. multi method head(Any:D: Callable:D $w) {
  1784. Seq.new(
  1785. Rakudo::Iterator.AllButLastNValues(self.iterator,-($w(0).Int))
  1786. )
  1787. }
  1788. multi method head(Any:D: $n) {
  1789. Seq.new(Rakudo::Iterator.NextNValues(self.iterator,$n))
  1790. }
  1791. proto method tail(|) { * }
  1792. multi method tail(Any:D:) is raw {
  1793. nqp::if(
  1794. nqp::eqaddr((my $pulled :=
  1795. Rakudo::Iterator.LastValue(self.iterator,'tail')),
  1796. IterationEnd
  1797. ),
  1798. Nil,
  1799. $pulled
  1800. )
  1801. }
  1802. multi method tail(Any:D: $n) {
  1803. Seq.new(
  1804. nqp::if(
  1805. nqp::istype($n,Callable)
  1806. && nqp::isgt_i((my $skip := -($n(0).Int)),0),
  1807. nqp::stmts(
  1808. (my $iterator := self.iterator).skip-at-least($skip),
  1809. $iterator
  1810. ),
  1811. Rakudo::Iterator.LastNValues(self.iterator,$n,'tail')
  1812. )
  1813. )
  1814. }
  1815. proto method minpairs(|) { * }
  1816. multi method minpairs(Any:D:) {
  1817. my @found;
  1818. for self.pairs {
  1819. my $value := .value;
  1820. state $min = $value;
  1821. nqp::if(
  1822. nqp::iseq_i( (my $cmp := $value cmp $min), -1 ),
  1823. nqp::stmts((@found = $_), ($min = $value)),
  1824. nqp::if(
  1825. nqp::iseq_i($cmp, 0),
  1826. @found.push($_)
  1827. )
  1828. )
  1829. }
  1830. Seq.new(@found.iterator)
  1831. }
  1832. proto method maxpairs(|) { * }
  1833. multi method maxpairs(Any:D:) {
  1834. my @found;
  1835. for self.pairs {
  1836. my $value := .value;
  1837. state $max = $value;
  1838. nqp::if(
  1839. nqp::iseq_i( (my $cmp := $value cmp $max), 1 ),
  1840. nqp::stmts((@found = $_), ($max = $value)),
  1841. nqp::if(
  1842. nqp::iseq_i($cmp, 0),
  1843. @found.push($_)
  1844. )
  1845. )
  1846. }
  1847. Seq.new(@found.iterator)
  1848. }
  1849. proto method batch(|) is nodal { * }
  1850. multi method batch(Any:D: Int:D :$elems!) {
  1851. Seq.new(Rakudo::Iterator.Batch(self.iterator,$elems,1))
  1852. }
  1853. multi method batch(Any:D: Int:D $batch) {
  1854. Seq.new(Rakudo::Iterator.Batch(self.iterator,$batch,1))
  1855. }
  1856. proto method rotor(|) is nodal { * }
  1857. multi method rotor(Any:D: Int:D $batch, :$partial) {
  1858. Seq.new(Rakudo::Iterator.Batch(self.iterator,$batch,$partial))
  1859. }
  1860. multi method rotor(Any:D: *@cycle, :$partial) {
  1861. Seq.new(Rakudo::Iterator.Rotor(self.iterator,@cycle,$partial))
  1862. }
  1863. proto method skip(|) { * }
  1864. multi method skip() { Seq.new(self.iterator).skip }
  1865. multi method skip(Int() $n) { Seq.new(self.iterator).skip($n) }
  1866. }
  1867. BEGIN Attribute.^compose;
  1868. proto sub infix:<min>(|) is pure { * }
  1869. multi sub infix:<min>(Mu:D \a, Mu:U) { a }
  1870. multi sub infix:<min>(Mu:U, Mu:D \b) { b }
  1871. multi sub infix:<min>(Mu:D \a, Mu:D \b) { (a cmp b) < 0 ?? a !! b }
  1872. multi sub infix:<min>(Int:D \a, Int:D \b) { nqp::if(nqp::islt_i(nqp::cmp_I(nqp::decont(a), nqp::decont(b)), 0), a, b) }
  1873. multi sub infix:<min>(int \a, int \b) { nqp::if(nqp::islt_i(nqp::cmp_i(a, b), 0), a, b) }
  1874. multi sub infix:<min>(Num:D \a, Num:D \b) { nqp::if(nqp::islt_i(nqp::cmp_n(a, b), 0), a, b) }
  1875. multi sub infix:<min>(num \a, num \b) { nqp::if(nqp::islt_i(nqp::cmp_n(a, b), 0), a, b) }
  1876. multi sub infix:<min>(+args is raw) { args.min }
  1877. sub min(+args, :&by = &infix:<cmp>) { args.min(&by) }
  1878. proto sub infix:<max>(|) is pure { * }
  1879. multi sub infix:<max>(Mu:D \a, Mu:U) { a }
  1880. multi sub infix:<max>(Mu:U, Mu:D \b) { b }
  1881. multi sub infix:<max>(Mu:D \a, Mu:D \b) { (a cmp b) > 0 ?? a !! b }
  1882. multi sub infix:<max>(Int:D \a, Int:D \b) { nqp::if(nqp::isgt_i(nqp::cmp_I(nqp::decont(a), nqp::decont(b)), 0), a, b) }
  1883. multi sub infix:<max>(int \a, int \b) { nqp::if(nqp::isgt_i(nqp::cmp_i(a, b), 0), a, b) }
  1884. multi sub infix:<max>(Num:D \a, Num:D \b) { nqp::if(nqp::isgt_i(nqp::cmp_n(a, b), 0), a, b) }
  1885. multi sub infix:<max>(num \a, num \b) { nqp::if(nqp::isgt_i(nqp::cmp_n(a, b), 0), a, b) }
  1886. multi sub infix:<max>(+args) { args.max }
  1887. sub max(+args, :&by = &infix:<cmp>) { args.max(&by) }
  1888. proto sub infix:<minmax>(|) is pure { * }
  1889. multi sub infix:<minmax>(+args) { args.minmax }
  1890. sub minmax(+args, :&by = &infix:<cmp>) { args.minmax(&by) }
  1891. proto sub map(|) {*}
  1892. multi sub map(&code, +values) { my $laze = values.is-lazy; values.map(&code).lazy-if($laze) }
  1893. proto sub grep(|) {*}
  1894. multi sub grep(Mu $test, +values, *%a) {
  1895. my $laze = values.is-lazy;
  1896. values.grep($test,|%a).lazy-if($laze)
  1897. }
  1898. multi sub grep(Bool:D $t, |) { X::Match::Bool.new(:type<grep>).throw }
  1899. proto sub first(|) {*}
  1900. multi sub first(Bool:D $t, |) { Failure.new(X::Match::Bool.new(:type<first>)) }
  1901. multi sub first(Mu $test, +values, *%a) { values.first($test,|%a) }
  1902. proto sub join(|) { * }
  1903. multi sub join($sep = '', *@values) { @values.join($sep) }
  1904. proto sub reduce (|) { * }
  1905. multi sub reduce (&with, +list) { list.reduce(&with) }
  1906. proto sub produce (|) { * }
  1907. multi sub produce (&with, +list) { list.produce(&with) }
  1908. proto sub unique(|) { * }
  1909. multi sub unique(+values, |c) { my $laze = values.is-lazy; values.unique(|c).lazy-if($laze) }
  1910. proto sub squish(|) { * }
  1911. multi sub squish(+values, |c) { my $laze = values.is-lazy; values.squish(|c).lazy-if($laze) }
  1912. proto sub repeated(|) { * }
  1913. multi sub repeated(+values, |c) { my $laze = values.is-lazy; values.repeated(|c).lazy-if($laze) }
  1914. proto sub sort(|) {*}
  1915. multi sub sort(&by, @values) { @values.sort(&by) }
  1916. multi sub sort(&by, +values) { values.sort(&by) }
  1917. multi sub sort(@values) { @values.sort }
  1918. multi sub sort(+values) { values.sort }