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