1. sub METAOP_ASSIGN(\op) {
  2. -> Mu \a, Mu \b { a = op.( ( a.DEFINITE ?? a !! op.() ), b) }
  3. }
  4. sub METAOP_TEST_ASSIGN:<//>(\lhs, $rhs) is raw { lhs // (lhs = $rhs()) }
  5. sub METAOP_TEST_ASSIGN:<||>(\lhs, $rhs) is raw { lhs || (lhs = $rhs()) }
  6. sub METAOP_TEST_ASSIGN:<&&>(\lhs, $rhs) is raw { lhs && (lhs = $rhs()) }
  7. sub METAOP_TEST_ASSIGN:<or>(\lhs, $rhs) is raw { lhs or (lhs = $rhs()) }
  8. sub METAOP_TEST_ASSIGN:<and>( \lhs, $rhs) is raw { lhs and (lhs = $rhs()) }
  9. sub METAOP_TEST_ASSIGN:<andthen>( \lhs, $rhs) is raw { lhs andthen (lhs = $rhs()) }
  10. sub METAOP_TEST_ASSIGN:<notandthen>(\lhs, $rhs) is raw { lhs notandthen (lhs = $rhs()) }
  11. sub METAOP_TEST_ASSIGN:<orelse>( \lhs, $rhs) is raw { lhs orelse (lhs = $rhs()) }
  12. sub METAOP_NEGATE(\op) {
  13. -> |c { c.elems > 1 ?? !op.(|c) !! True }
  14. }
  15. sub METAOP_REVERSE(\op) {
  16. -> |args { op.(|args.reverse) }
  17. }
  18. sub METAOP_CROSS(\op, &reduce) {
  19. nqp::if(op.prec('thunky').starts-with('.'),
  20. -> +lol {
  21. my $rop = lol.elems == 2 ?? op !! &reduce(op);
  22. my $laze = False;
  23. my @loi = eager for lol -> \elem {
  24. if nqp::iscont(elem) {
  25. $laze = False;
  26. (elem,).iterator
  27. }
  28. else {
  29. $laze = True if elem.is-lazy;
  30. elem.iterator
  31. }
  32. }
  33. my Mu $cache := nqp::list();
  34. my int $i = 0;
  35. for ^lol.elems {
  36. $i = $_;
  37. my Mu $rpa := nqp::list();
  38. nqp::bindpos($cache, $i, $rpa);
  39. }
  40. my int $n = lol.elems - 1;
  41. my $j = 0;
  42. my @j;
  43. my @v;
  44. $i = 0;
  45. gather {
  46. while $i >= 0 {
  47. my Mu $sublist := nqp::atpos($cache, $i);
  48. if $j < nqp::elems($sublist) {
  49. my Mu $o := nqp::atpos($sublist, $j);
  50. @v[$i] := $o;
  51. $j = $j + 1;
  52. if $i >= $n { take lol.elems == 2 ?? $rop(|@v) !! $rop(@v); }
  53. else { $i = $i + 1; @j.push($j); $j = 0; }
  54. }
  55. elsif nqp::not_i(nqp::eqaddr((my \value = @loi[$i].pull-one),IterationEnd)) {
  56. nqp::bindpos($sublist, $j, value);
  57. redo;
  58. }
  59. else {
  60. $i = $i - 1;
  61. if $i { $j = @j.pop if $i > 0 } # continue previous dimension where we left off
  62. else {
  63. $j = 0;
  64. my Mu $sublist := nqp::atpos($cache,$i);
  65. nqp::pop($sublist); # don't cache 1st dimension (could be infinite)
  66. }
  67. }
  68. }
  69. }.lazy-if($laze);
  70. },
  71. -> +lol {
  72. Seq.new(Rakudo::Iterator.CrossIterablesOp(lol,op))
  73. }
  74. )
  75. }
  76. sub METAOP_ZIP(\op, &reduce) {
  77. nqp::if(op.prec('thunky').starts-with('.'),
  78. -> +lol {
  79. my $arity = lol.elems;
  80. my $rop = $arity == 2 ?? op !! &reduce(op);
  81. my $laze = True;
  82. my @loi = eager for lol -> \elem {
  83. if nqp::iscont(elem) {
  84. $laze = False;
  85. Rakudo::Iterator.OneValue(elem)
  86. }
  87. else {
  88. $laze = False unless elem.is-lazy;
  89. Rakudo::Iterator.Whatever(elem.iterator)
  90. }
  91. }
  92. gather {
  93. loop {
  94. my \z = @loi.map: {
  95. my \value = .pull-one;
  96. last if nqp::eqaddr(value,IterationEnd);
  97. value
  98. };
  99. my $z = List.from-iterator(z.iterator);
  100. $z.eager;
  101. last if $z.elems < $arity;
  102. take-rw $arity == 2 ?? $rop(|$z) !! $rop(@$z);
  103. }
  104. }.lazy-if($laze)
  105. },
  106. -> +lol {
  107. Seq.new(Rakudo::Iterator.ZipIterablesOp(lol,op))
  108. }
  109. )
  110. }
  111. proto sub METAOP_REDUCE_LEFT(|) { * }
  112. multi sub METAOP_REDUCE_LEFT(\op, \triangle) {
  113. if op.count > 2 and op.count < Inf {
  114. my $count = op.count;
  115. sub (+values) {
  116. my \source = values.iterator;
  117. my \first = source.pull-one;
  118. return () if nqp::eqaddr(first,IterationEnd);
  119. my @args.push: first;
  120. GATHER({
  121. take first;
  122. until nqp::eqaddr((my \current = source.pull-one),IterationEnd) {
  123. @args.push: current;
  124. if @args.elems == $count {
  125. my \val = op.(|@args);
  126. take val;
  127. @args = ();
  128. @args.push: val; # use of push allows op to return a Slip
  129. }
  130. }
  131. }).lazy-if(source.is-lazy);
  132. }
  133. }
  134. else {
  135. sub (+values) {
  136. my \source = values.iterator;
  137. my \first = source.pull-one;
  138. return () if nqp::eqaddr(first,IterationEnd);
  139. my $result := first;
  140. GATHER({
  141. take first;
  142. until nqp::eqaddr((my \value = source.pull-one),IterationEnd) {
  143. take ($result := op.($result, value));
  144. }
  145. }).lazy-if(source.is-lazy);
  146. }
  147. }
  148. }
  149. multi sub METAOP_REDUCE_LEFT(\op) {
  150. if op.count > 2 and op.count < Inf {
  151. my $count = op.count;
  152. sub (+values) {
  153. my \iter = values.iterator;
  154. my \first = iter.pull-one;
  155. return op.() if nqp::eqaddr(first,IterationEnd);
  156. my @args.push: first;
  157. my $result := first;
  158. until nqp::eqaddr((my \value = iter.pull-one),IterationEnd) {
  159. @args.push: value;
  160. if @args.elems == $count {
  161. my \val = op.(|@args);
  162. @args = ();
  163. @args.push: val; # use of push allows op to return a Slip
  164. $result := val;
  165. }
  166. }
  167. $result;
  168. }
  169. }
  170. else {
  171. nqp::eqaddr(op,&infix:<+>)
  172. ?? &sum
  173. !! sub (+values) {
  174. nqp::stmts(
  175. (my $iter := values.iterator),
  176. nqp::if(
  177. nqp::eqaddr((my $result := $iter.pull-one),IterationEnd),
  178. op.(), # identity
  179. nqp::if(
  180. nqp::eqaddr((my $value := $iter.pull-one),IterationEnd),
  181. nqp::if(
  182. nqp::isle_i(op.arity,1),
  183. op.($result), # can call with 1 param
  184. $result # what we got
  185. ),
  186. nqp::stmts(
  187. ($result := op.($result,$value)),
  188. nqp::until(
  189. nqp::eqaddr(($value := $iter.pull-one),IterationEnd),
  190. ($result := op.($result,$value))
  191. ),
  192. $result # final result
  193. )
  194. )
  195. )
  196. )
  197. }
  198. }
  199. }
  200. proto sub METAOP_REDUCE_RIGHT(|) { * }
  201. multi sub METAOP_REDUCE_RIGHT(\op, \triangle) {
  202. nqp::if(
  203. op.count < Inf && nqp::isgt_i((my int $count = op.count),2),
  204. sub (+values) {
  205. Seq.new(nqp::if(
  206. nqp::isge_i((my int $i = (my $v :=
  207. nqp::if(nqp::istype(values,List),values,values.List)
  208. ).elems), # reifies
  209. $count
  210. ), # reifies
  211. class :: does Iterator {
  212. has $!op;
  213. has $!reified;
  214. has $!result;
  215. has int $!count;
  216. has int $!i;
  217. method !SET-SELF(\op,\list,\count,\index) {
  218. nqp::stmts(
  219. ($!op := op),
  220. ($!reified := nqp::getattr(list,List,'$!reified')),
  221. ($!count = count),
  222. ($!i = index),
  223. self
  224. )
  225. }
  226. method new(\op,\list,\count,\index) {
  227. nqp::create(self)!SET-SELF(op,list,count,index)
  228. }
  229. method pull-one() is raw {
  230. nqp::if(
  231. nqp::attrinited(self,self.WHAT,'$!result'),
  232. nqp::stmts(
  233. (my $args := nqp::list($!result)),
  234. nqp::until(
  235. nqp::iseq_i(nqp::elems($args),$!count)
  236. || nqp::islt_i(($!i = nqp::sub_i($!i,1)),0),
  237. nqp::unshift($args,nqp::atpos($!reified,$!i))
  238. ),
  239. nqp::if(
  240. nqp::isgt_i(nqp::elems($args),1),
  241. ($!result := op.(|nqp::hllize($args))),
  242. IterationEnd
  243. )
  244. ),
  245. ($!result := nqp::atpos(
  246. $!reified,
  247. ($!i = nqp::sub_i($!i,1))
  248. ))
  249. )
  250. }
  251. method bool-only(--> True) { };
  252. method count-only() { nqp::p6box_i($!i) }
  253. }.new(op,$v,$count,$i),
  254. Rakudo::Iterator.OneValue(
  255. nqp::if(
  256. $i,
  257. op.(|nqp::getattr($v,List,'$!reified')),
  258. op.()
  259. )
  260. )
  261. ))
  262. },
  263. sub (+values) {
  264. Seq.new(nqp::if(
  265. nqp::isgt_i((my int $i = (my $v :=
  266. nqp::if(nqp::istype(values,List),values,values.List)
  267. ).elems), # reifies
  268. 1
  269. ),
  270. class :: does Iterator {
  271. has $!op;
  272. has $!reified;
  273. has $!result;
  274. has int $!i;
  275. method !SET-SELF(\op,\list,\count) {
  276. nqp::stmts(
  277. ($!op := op),
  278. ($!reified := nqp::getattr(list,List,'$!reified')),
  279. ($!i = count),
  280. self
  281. )
  282. }
  283. method new(\op,\li,\co) { nqp::create(self)!SET-SELF(op,li,co) }
  284. method pull-one() is raw {
  285. nqp::if(
  286. nqp::attrinited(self,self.WHAT,'$!result'),
  287. nqp::if(
  288. nqp::isge_i(($!i = nqp::sub_i($!i,1)),0),
  289. ($!result := $!op.(nqp::atpos($!reified,$!i),$!result)),
  290. IterationEnd
  291. ),
  292. ($!result := nqp::atpos(
  293. $!reified,
  294. ($!i = nqp::sub_i($!i,1))
  295. ))
  296. )
  297. }
  298. method bool-only(--> True) { };
  299. method count-only() { nqp::p6box_i($!i) }
  300. }.new(op,$v,$i),
  301. Rakudo::Iterator.OneValue(
  302. nqp::if(
  303. $i,
  304. op.(nqp::atpos(nqp::getattr($v,List,'$!reified'),0)),
  305. op.()
  306. )
  307. )
  308. ))
  309. }
  310. )
  311. }
  312. multi sub METAOP_REDUCE_RIGHT(\op) {
  313. nqp::if(
  314. op.count < Inf && nqp::isgt_i((my int $count = op.count),2),
  315. sub (+values) {
  316. nqp::if(
  317. nqp::isge_i((my int $i = (my $v :=
  318. nqp::if(nqp::istype(values,List),values,values.List)
  319. ).elems), # reifies
  320. $count
  321. ), # reifies
  322. nqp::stmts(
  323. (my $args := nqp::list(
  324. my $result := nqp::atpos(
  325. (my $reified := nqp::getattr($v,List,'$!reified')),
  326. ($i = nqp::sub_i($i,1))
  327. )
  328. )),
  329. nqp::until(
  330. nqp::islt_i(($i = nqp::sub_i($i,1)),0),
  331. nqp::stmts(
  332. nqp::unshift($args,nqp::atpos($reified,$i)),
  333. nqp::if(
  334. nqp::iseq_i(nqp::elems($args),$count),
  335. nqp::stmts(
  336. ($result := op.(|nqp::hllize($args))),
  337. nqp::bindpos(nqp::setelems($args,1),0,$result)
  338. )
  339. )
  340. )
  341. ),
  342. nqp::if(
  343. nqp::isgt_i(nqp::elems($args),1),
  344. op.(|nqp::hllize($args)), # something left to process
  345. $result
  346. )
  347. ),
  348. nqp::if(
  349. $i,
  350. op.(|nqp::getattr($v,List,'$!reified')),
  351. op.()
  352. )
  353. )
  354. },
  355. sub (+values) {
  356. nqp::if(
  357. nqp::isgt_i((my int $i = (my $v :=
  358. nqp::if(nqp::istype(values,List),values,values.List)
  359. ).elems), # reifies
  360. 1
  361. ),
  362. nqp::stmts(
  363. (my $result := nqp::atpos(
  364. nqp::getattr($v,List,'$!reified'),
  365. ($i = nqp::sub_i($i,1))
  366. )),
  367. nqp::while(
  368. nqp::isge_i(($i = nqp::sub_i($i,1)),0),
  369. ($result := op.(
  370. nqp::atpos(nqp::getattr($v,List,'$!reified'),$i),
  371. $result
  372. ))
  373. ),
  374. $result
  375. ),
  376. nqp::if(
  377. $i,
  378. op.(nqp::atpos(nqp::getattr($v,List,'$!reified'),0)),
  379. op.()
  380. )
  381. )
  382. }
  383. )
  384. }
  385. proto sub METAOP_REDUCE_LIST(|) { * }
  386. multi sub METAOP_REDUCE_LIST(\op, \triangle) {
  387. sub (+values) {
  388. GATHER({
  389. my @list;
  390. for values -> \v {
  391. @list.push(v);
  392. take op.(|@list);
  393. }
  394. }).lazy-if(values.is-lazy);
  395. }
  396. }
  397. multi sub METAOP_REDUCE_LIST(\op) {
  398. sub (+values) { op.(|values) }
  399. }
  400. proto sub METAOP_REDUCE_LISTINFIX(|) { * }
  401. multi sub METAOP_REDUCE_LISTINFIX(\op, \triangle) {
  402. sub (|values) {
  403. my \p = values[0];
  404. return () unless p.elems;
  405. my int $i;
  406. GATHER({
  407. my @list;
  408. while $i < p.elems {
  409. @list.push(p[$i++]);
  410. take op.(|@list.map({nqp::decont($_)}));
  411. }
  412. }).lazy-if(p.is-lazy);
  413. }
  414. }
  415. multi sub METAOP_REDUCE_LISTINFIX(\op) {
  416. sub (+values) {
  417. op.(|values.map({nqp::decont($_)}));
  418. }
  419. }
  420. proto sub METAOP_REDUCE_CHAIN(|) { * }
  421. multi sub METAOP_REDUCE_CHAIN(\op, \triangle) {
  422. sub (+values) {
  423. my $state = True;
  424. my \iter = values.iterator;
  425. my Mu $current = iter.pull-one;
  426. gather {
  427. take $state;
  428. while $state && nqp::not_i(nqp::eqaddr((my $next := iter.pull-one),IterationEnd)) {
  429. $state = op.($current, $next);
  430. take $state;
  431. $current := $next;
  432. }
  433. unless $state {
  434. take False until nqp::eqaddr(iter.pull-one,IterationEnd);
  435. }
  436. }.lazy-if(values.is-lazy);
  437. }
  438. }
  439. multi sub METAOP_REDUCE_CHAIN(\op) {
  440. sub (+values) {
  441. nqp::if(
  442. nqp::eqaddr(
  443. (my $current := (my $iter := values.iterator).pull-one),
  444. IterationEnd
  445. ),
  446. True,
  447. nqp::stmts(
  448. nqp::while(
  449. nqp::not_i(nqp::eqaddr((my $next := $iter.pull-one),IterationEnd))
  450. && op.($current,$next),
  451. $current := $next
  452. ),
  453. nqp::p6bool(nqp::eqaddr($next,IterationEnd))
  454. )
  455. )
  456. }
  457. }
  458. sub METAOP_REDUCE_XOR(\op, $triangle?) {
  459. X::NYI.new(feature => 'xor reduce').throw;
  460. }
  461. sub METAOP_HYPER(\op, *%opt) {
  462. -> Mu \a, Mu \b { HYPER(op, a, b, |%opt) }
  463. }
  464. proto sub METAOP_HYPER_POSTFIX(|) {*}
  465. multi sub METAOP_HYPER_POSTFIX(\op) {
  466. nqp::if(
  467. nqp::can(op,"nodal"),
  468. (-> \obj { nodemap(op, obj) }),
  469. (-> \obj { deepmap(op, obj) })
  470. )
  471. }
  472. # no indirection for subscripts and such
  473. proto sub METAOP_HYPER_POSTFIX_ARGS(|) {*}
  474. multi sub METAOP_HYPER_POSTFIX_ARGS(\obj,\op) {
  475. nqp::if(
  476. nqp::can(op,"nodal"),
  477. nodemap(op, obj),
  478. deepmap(op, obj)
  479. )
  480. }
  481. multi sub METAOP_HYPER_POSTFIX_ARGS(\obj, @args, \op) {
  482. nqp::if(
  483. nqp::can(op,"nodal"),
  484. nodemap( -> \o { op.(o,@args) }, obj ),
  485. deepmap( -> \o { op.(o,@args) }, obj )
  486. )
  487. }
  488. multi sub METAOP_HYPER_POSTFIX_ARGS(\obj, \args, \op) {
  489. nqp::if(
  490. nqp::can(op,"nodal"),
  491. nodemap( -> \o { op.(o,|args) }, obj ),
  492. deepmap( -> \o { op.(o,|args) }, obj )
  493. )
  494. }
  495. sub METAOP_HYPER_PREFIX(\op) {
  496. nqp::if(
  497. nqp::can(op,"nodal"), # rarely true for prefixes
  498. (-> \obj { nodemap(op, obj) }),
  499. (-> \obj { deepmap(op, obj) })
  500. )
  501. }
  502. sub METAOP_HYPER_CALL(\list, |args) { deepmap(-> $c { $c(|args) }, list) }
  503. proto sub HYPER(|) { * }
  504. multi sub HYPER(&op, \left, \right, :$dwim-left, :$dwim-right) {
  505. op(left, right);
  506. }
  507. multi sub HYPER(&op, Associative:D \left, Associative:D \right, :$dwim-left, :$dwim-right) {
  508. my %keyset;
  509. if !$dwim-left {
  510. %keyset{$_} = 1 for left.keys;
  511. }
  512. else {
  513. %keyset{$_} = 1 if right.EXISTS-KEY($_) for left.keys;
  514. }
  515. if !$dwim-right {
  516. %keyset{$_} = 1 for right.keys;
  517. }
  518. my @keys = %keyset.keys;
  519. my $type = left.WHAT;
  520. my \result := $type.new;
  521. result = quietly @keys Z=> HYPER(&op, left{@keys}, right{@keys}, :$dwim-left, :$dwim-right);
  522. nqp::iscont(left) ?? result.item !! result;
  523. }
  524. multi sub HYPER(&op, Associative:D \left, \right, :$dwim-left, :$dwim-right) {
  525. my @keys = left.keys;
  526. my $type = left.WHAT;
  527. my \result := $type.new;
  528. result = @keys Z=> HYPER(&op, left{@keys}, right, :$dwim-left, :$dwim-right);
  529. nqp::iscont(left) ?? result.item !! result;
  530. }
  531. multi sub HYPER(&op, \left, Associative:D \right, :$dwim-left, :$dwim-right) {
  532. my @keys = right.keys;
  533. my $type = right.WHAT;
  534. my \result := $type.new;
  535. result = @keys Z=> HYPER(&op, left, right{@keys}, :$dwim-left, :$dwim-right);
  536. nqp::iscont(right) ?? result.item !! result;
  537. }
  538. multi sub HYPER(&operator, Positional:D \left, \right, :$dwim-left, :$dwim-right) {
  539. my @result;
  540. X::HyperOp::Infinite.new(:side<left>, :&operator).throw if left.is-lazy;
  541. my int $elems = left.elems;
  542. X::HyperOp::NonDWIM.new(:&operator, :left-elems($elems), :right-elems(1), :recursing(callframe(3).code.name eq 'HYPER')).throw
  543. unless $elems == 1 or $elems > 1 and $dwim-right or $elems == 0 and $dwim-left || $dwim-right;
  544. my \lefti := left.iterator;
  545. my int $i = 0;
  546. until nqp::eqaddr((my \value := lefti.pull-one),IterationEnd) {
  547. @result[$i++] := HYPER(&operator, value, right, :$dwim-left, :$dwim-right);
  548. }
  549. # Coerce to the original type if it's a subtype of List
  550. my $type = nqp::istype(left, List) ?? left.WHAT !! List;
  551. nqp::iscont(left) ?? $type(|@result.eager).item !! $type(|@result.eager)
  552. }
  553. multi sub HYPER(&operator, \left, Positional:D \right, :$dwim-left, :$dwim-right) {
  554. my @result;
  555. X::HyperOp::Infinite.new(:side<right>, :&operator).throw if right.is-lazy;
  556. my int $elems = right.elems;
  557. X::HyperOp::NonDWIM.new(:&operator, :left-elems(1), :right-elems($elems), :recursing(callframe(3).code.name eq 'HYPER')).throw
  558. unless $elems == 1 or $elems > 1 and $dwim-left or $elems == 0 and $dwim-left || $dwim-right;
  559. my \righti := right.iterator;
  560. my int $i = 0;
  561. until nqp::eqaddr((my \value := righti.pull-one),IterationEnd) {
  562. @result[$i++] := HYPER(&operator, left, value, :$dwim-left, :$dwim-right);
  563. }
  564. # Coerce to the original type if it's a subtype of List
  565. my $type = nqp::istype(right, List) ?? right.WHAT !! List;
  566. nqp::iscont(right) ?? $type(|@result.eager).item !! $type(|@result.eager)
  567. }
  568. multi sub HYPER(&operator, Iterable:D \left, Iterable:D \right, :$dwim-left, :$dwim-right) {
  569. my \left-iterator = left.iterator;
  570. my \right-iterator = right.iterator;
  571. # Check whether any side is lazy. They must not be to proceed.
  572. if left-iterator.is-lazy {
  573. X::HyperOp::Infinite.new(:side<both>, :&operator).throw if right-iterator.is-lazy;
  574. X::HyperOp::Infinite.new(:side<left>, :&operator).throw if not $dwim-left or $dwim-right;
  575. }
  576. X::HyperOp::Infinite.new(:side<right>, :&operator).throw if right-iterator.is-lazy and
  577. (not $dwim-right or $dwim-left);
  578. my \lefti := Rakudo::Iterator.DWIM(left-iterator);
  579. my \righti := Rakudo::Iterator.DWIM(right-iterator);
  580. my \result := IterationBuffer.new;
  581. loop {
  582. my \leftv := lefti.pull-one;
  583. my \rightv := righti.pull-one;
  584. X::HyperOp::NonDWIM.new(:&operator, :left-elems(lefti.count-elems), :right-elems(righti.count-elems), :recursing(callframe(3).code.name eq 'HYPER')).throw
  585. if !$dwim-left and !$dwim-right and (lefti.ended != righti.ended);
  586. last if ($dwim-left and $dwim-right) ?? (lefti.ended and righti.ended) !!
  587. (($dwim-left or lefti.ended) and ($dwim-right or righti.ended));
  588. last if $++ == 0 and ($dwim-left and lefti.ended or $dwim-right and righti.ended);
  589. nqp::push(result,HYPER(&operator, leftv, rightv, :$dwim-left, :$dwim-right));
  590. }
  591. # Coerce to the original type
  592. my $type = nqp::istype(left, List) ?? left.WHAT !! List; # keep subtypes of List
  593. my \retval = $type.new;
  594. nqp::bindattr(retval, List, '$!reified', result);
  595. nqp::iscont(left) ?? retval.item !! retval;
  596. }
  597. multi sub HYPER(\op, \obj) {
  598. nqp::if(
  599. nqp::can(op,"nodal"),
  600. nodemap(op, obj),
  601. deepmap(op,obj)
  602. )
  603. }
  604. proto sub deepmap(|) { * }
  605. multi sub deepmap(\op, \obj) {
  606. Rakudo::Internals.coremap(op, obj, :deep)
  607. }
  608. multi sub deepmap(\op, Associative \h) {
  609. my @keys = h.keys;
  610. hash @keys Z deepmap(op, h{@keys})
  611. }
  612. proto sub nodemap(|) { * }
  613. multi sub nodemap(\op, \obj) {
  614. my Mu $rpa := nqp::create(IterationBuffer);
  615. my \objs := obj.list;
  616. # as a wanted side-effect is-lazy reifies the list
  617. fail X::Cannot::Lazy.new(:action('deepmap')) if objs.is-lazy;
  618. my Mu $items := nqp::getattr(objs, List, '$!reified');
  619. my Mu $o;
  620. # We process the elements in two passes, end to start, to
  621. # prevent users from relying on a sequential ordering of hyper.
  622. # Also, starting at the end pre-allocates $rpa for us.
  623. my int $i = nqp::elems($items) - 1;
  624. nqp::while(
  625. nqp::isge_i($i, 0),
  626. nqp::stmts(
  627. ($o := nqp::atpos($items, $i)),
  628. nqp::bindpos($rpa, $i,
  629. nqp::if(Mu, # hack cuz I don't understand nqp
  630. $o.new(nodemap(op, $o)).item,
  631. op.($o))),
  632. $i = nqp::sub_i($i, 2)
  633. )
  634. );
  635. $i = nqp::elems($items) - 2;
  636. nqp::while(
  637. nqp::isge_i($i, 0),
  638. nqp::stmts(
  639. ($o := nqp::atpos($items, $i)),
  640. nqp::bindpos($rpa, $i,
  641. nqp::if(Mu, # hack cuz I don't understand nqp
  642. $o.new(nodemap(op, $o)).item,
  643. op.($o))),
  644. $i = nqp::sub_i($i, 2)
  645. )
  646. );
  647. nqp::p6bindattrinvres(nqp::create(List), List, '$!reified', $rpa)
  648. }
  649. multi sub nodemap(\op, Associative \h) {
  650. my @keys = h.keys;
  651. hash @keys Z nodemap(op, h{@keys})
  652. }
  653. proto sub duckmap(|) { * }
  654. multi sub duckmap(\op, \obj) {
  655. Rakudo::Internals.coremap(sub (\arg) { CATCH { return arg ~~ Iterable:D ?? duckmap(op,arg) !! arg }; op.(arg); }, obj);
  656. }
  657. multi sub duckmap(\op, Associative \h) {
  658. my @keys = h.keys;
  659. hash @keys Z duckmap(op, h{@keys})
  660. }