1. # for our tantrums
  2. my class X::TypeCheck { ... };
  3. my class X::TypeCheck::Splice { ... }
  4. my class X::Subscript::Negative { ... };
  5. my class X::NotEnoughDimensions { ... };
  6. my class X::Assignment::ArrayShapeMismatch { ... };
  7. # stub what we need now
  8. my class array is repr('VMArray') { ... };
  9. # An Array is a List that ensures every item added to it is in a Scalar
  10. # container. It also supports push, pop, shift, unshift, splice, BIND-POS,
  11. # and so forth.
  12. my class Array { # declared in BOOTSTRAP
  13. # class Array is List
  14. # has Mu $!descriptor;
  15. my class ArrayReificationTarget {
  16. has $!target;
  17. has $!descriptor;
  18. method new(\target, Mu \descriptor) {
  19. nqp::stmts(
  20. nqp::bindattr((my \rt = nqp::create(self)),
  21. self,'$!target',target),
  22. nqp::p6bindattrinvres(rt,
  23. self,'$!descriptor',descriptor)
  24. )
  25. }
  26. method push(Mu \value) {
  27. nqp::push($!target,
  28. nqp::assign(nqp::p6scalarfromdesc($!descriptor), value));
  29. }
  30. }
  31. my class ListReificationTarget {
  32. has $!target;
  33. method new(\target) {
  34. nqp::p6bindattrinvres(nqp::create(self), self, '$!target', target);
  35. }
  36. method push(Mu \value) {
  37. nqp::push($!target,
  38. nqp::decont(value));
  39. }
  40. }
  41. multi method clone(Array:D:) { [self] }
  42. method iterator(Array:D:) {
  43. # something to iterate over in the future
  44. if nqp::getattr(self,List,'$!todo').DEFINITE {
  45. class :: does Iterator {
  46. has int $!i;
  47. has $!array;
  48. has $!reified;
  49. has $!todo;
  50. has $!descriptor;
  51. method !SET-SELF(\array) {
  52. $!i = -1;
  53. $!array := array;
  54. $!reified :=
  55. nqp::ifnull(
  56. nqp::getattr( array,List,'$!reified'),
  57. nqp::bindattr(array,List,'$!reified',
  58. nqp::create(IterationBuffer))
  59. );
  60. $!todo := nqp::getattr(array,List, '$!todo');
  61. $!descriptor := nqp::getattr(array,Array,'$!descriptor');
  62. self
  63. }
  64. method new(\array) { nqp::create(self)!SET-SELF(array) }
  65. method pull-one() is raw {
  66. nqp::ifnull(
  67. nqp::atpos($!reified,$!i = nqp::add_i($!i,1)),
  68. nqp::islt_i($!i,nqp::elems($!reified))
  69. ?? self!found-hole
  70. !! $!todo.DEFINITE
  71. ?? nqp::islt_i($!i,$!todo.reify-at-least(nqp::add_i($!i,1)))
  72. ?? nqp::atpos($!reified,$!i) # cannot be nqp::null
  73. !! self!done
  74. !! IterationEnd
  75. )
  76. }
  77. method !found-hole() {
  78. nqp::p6bindattrinvres(
  79. (my \v := nqp::p6scalarfromdesc($!descriptor)),
  80. Scalar,
  81. '$!whence',
  82. -> { nqp::bindpos($!reified,$!i,v) }
  83. )
  84. }
  85. method !done() is raw {
  86. $!todo := nqp::bindattr($!array,List,'$!todo',Mu);
  87. IterationEnd
  88. }
  89. method push-until-lazy($target) {
  90. if $!todo.DEFINITE {
  91. my int $elems = $!todo.reify-until-lazy;
  92. nqp::while( # doesn't sink
  93. nqp::islt_i($!i = nqp::add_i($!i,1),$elems),
  94. $target.push(nqp::atpos($!reified,$!i))
  95. );
  96. nqp::if(
  97. $!todo.fully-reified,
  98. self!done,
  99. nqp::stmts(
  100. ($!i = nqp::sub_i($elems,1)),
  101. Mu
  102. )
  103. )
  104. }
  105. else {
  106. my int $elems = nqp::elems($!reified);
  107. nqp::while( # doesn't sink
  108. nqp::islt_i($!i = nqp::add_i($!i,1),$elems),
  109. $target.push(
  110. nqp::ifnull(
  111. nqp::atpos($!reified,$!i),
  112. nqp::p6bindattrinvres(
  113. (my \v := nqp::p6scalarfromdesc($!descriptor)),
  114. Scalar,
  115. '$!whence',
  116. -> { nqp::bindpos($!reified,$!i,v) }
  117. )
  118. )
  119. )
  120. );
  121. IterationEnd
  122. }
  123. }
  124. method is-lazy() { $!todo.DEFINITE && $!todo.is-lazy }
  125. }.new(self)
  126. }
  127. # everything we need is already there
  128. elsif nqp::getattr(self,List,'$!reified').DEFINITE {
  129. Rakudo::Iterator.ReifiedArray(self)
  130. }
  131. # nothing now or in the future to iterate over
  132. else {
  133. Rakudo::Iterator.Empty
  134. }
  135. }
  136. method from-iterator(Array:U: Iterator $iter) {
  137. nqp::if(
  138. nqp::eqaddr(
  139. $iter.push-until-lazy(
  140. my \target := ArrayReificationTarget.new(
  141. (my \buffer := nqp::create(IterationBuffer)),
  142. nqp::null
  143. )
  144. ),
  145. IterationEnd
  146. ),
  147. nqp::p6bindattrinvres(nqp::create(self),List,'$!reified',buffer),
  148. nqp::stmts(
  149. nqp::bindattr((my \result := nqp::create(self)),
  150. List,'$!reified',buffer),
  151. nqp::bindattr((my \todo := nqp::create(List::Reifier)),
  152. List::Reifier,'$!current-iter',$iter),
  153. nqp::bindattr(todo,
  154. List::Reifier,'$!reified',buffer),
  155. nqp::bindattr(todo,
  156. List::Reifier,'$!reification-target',target),
  157. nqp::p6bindattrinvres(result,List,'$!todo',todo)
  158. )
  159. )
  160. }
  161. proto method new(|) { * }
  162. multi method new(:$shape!) {
  163. nqp::if(
  164. nqp::defined($shape),
  165. set-shape(self,$shape),
  166. nqp::if(
  167. Metamodel::EnumHOW.ACCEPTS($shape.HOW),
  168. set-shape(self,$shape.^elems),
  169. nqp::create(self)
  170. )
  171. )
  172. }
  173. multi method new() {
  174. nqp::create(self)
  175. }
  176. multi method new(\values, :$shape!) {
  177. nqp::if(
  178. nqp::defined($shape),
  179. set-shape(self,$shape),
  180. nqp::if(
  181. Metamodel::EnumHOW.ACCEPTS($shape.HOW),
  182. set-shape(self,$shape.^elems),
  183. nqp::create(self)
  184. )
  185. ).STORE(values)
  186. }
  187. multi method new(\values) {
  188. nqp::create(self).STORE(values)
  189. }
  190. multi method new(**@values is raw, :$shape!) {
  191. nqp::if(
  192. nqp::defined($shape),
  193. set-shape(self,$shape),
  194. nqp::if(
  195. Metamodel::EnumHOW.ACCEPTS($shape.HOW),
  196. set-shape(self,$shape.^elems),
  197. nqp::create(self)
  198. )
  199. ).STORE(@values)
  200. }
  201. multi method new(**@values is raw) {
  202. nqp::create(self).STORE(@values)
  203. }
  204. proto method STORE(|) { * }
  205. multi method STORE(Array:D: Iterable:D \iterable) {
  206. nqp::iscont(iterable)
  207. ?? self!STORE-ONE(iterable)
  208. !! self!STORE-ITERABLE(iterable)
  209. }
  210. multi method STORE(Array:D: Mu \item) {
  211. self!STORE-ONE(item)
  212. }
  213. method !STORE-ITERABLE(\iterable) {
  214. my \new-storage = nqp::create(IterationBuffer);
  215. my \iter = iterable.iterator;
  216. my \target = ArrayReificationTarget.new(new-storage,
  217. nqp::decont($!descriptor));
  218. if iter.push-until-lazy(target) =:= IterationEnd {
  219. nqp::bindattr(self, List, '$!todo', Mu);
  220. }
  221. else {
  222. my \new-todo = nqp::create(List::Reifier);
  223. nqp::bindattr(new-todo, List::Reifier, '$!reified', new-storage);
  224. nqp::bindattr(new-todo, List::Reifier, '$!current-iter', iter);
  225. nqp::bindattr(new-todo, List::Reifier, '$!reification-target', target);
  226. nqp::bindattr(self, List, '$!todo', new-todo);
  227. }
  228. nqp::bindattr(self, List, '$!reified', new-storage);
  229. self
  230. }
  231. method !STORE-ONE(Mu \item) {
  232. my \new-storage = nqp::create(IterationBuffer);
  233. nqp::push(new-storage,
  234. nqp::assign(nqp::p6scalarfromdesc($!descriptor), item));
  235. nqp::bindattr(self, List, '$!reified', new-storage);
  236. nqp::bindattr(self, List, '$!todo', Mu);
  237. self
  238. }
  239. method reification-target() {
  240. ArrayReificationTarget.new(
  241. nqp::getattr(self, List, '$!reified'),
  242. nqp::decont($!descriptor))
  243. }
  244. multi method flat(Array:U:) { self }
  245. multi method flat(Array:D:) { Seq.new(self.iterator) }
  246. multi method List(Array:D: :$view) {
  247. nqp::if(
  248. self.is-lazy, # can't make a List
  249. X::Cannot::Lazy.new(:action<List>).throw,
  250. nqp::if( # all reified
  251. (my $reified := nqp::getattr(self,List,'$!reified')).DEFINITE,
  252. nqp::if(
  253. $view, # assume no change in array
  254. nqp::p6bindattrinvres(
  255. nqp::create(List),List,'$!reified',$reified),
  256. nqp::stmts( # make cow copy
  257. (my int $elems = nqp::elems($reified)),
  258. (my $cow := nqp::setelems(nqp::create(IterationBuffer),$elems)),
  259. (my int $i = -1),
  260. nqp::while(
  261. nqp::islt_i(($i = nqp::add_i($i,1)),$elems),
  262. nqp::bindpos($cow,$i,nqp::decont(nqp::atpos($reified,$i))),
  263. ),
  264. nqp::p6bindattrinvres(nqp::create(List),List,'$!reified',$cow)
  265. )
  266. ),
  267. nqp::create(List) # was empty, is empty
  268. )
  269. )
  270. }
  271. method shape() { (*,) }
  272. multi method AT-POS(Array:D: int $pos) is raw {
  273. nqp::if(
  274. nqp::islt_i($pos,0),
  275. Failure.new(X::OutOfRange.new(
  276. :what($*INDEX // 'Index'),:got($pos),:range<0..^Inf>)),
  277. nqp::if(
  278. (my $reified := nqp::getattr(self,List,'$!reified')).DEFINITE,
  279. nqp::ifnull(
  280. nqp::atpos($reified,$pos), # found it!
  281. nqp::if(
  282. nqp::islt_i(
  283. $pos,nqp::elems(nqp::getattr(self,List,'$!reified'))),
  284. self!AT-POS-CONTAINER($pos), # it's a hole
  285. nqp::if( # too far out, try reifying
  286. (my $todo := nqp::getattr(self,List,'$!todo')).DEFINITE,
  287. nqp::stmts(
  288. $todo.reify-at-least(nqp::add_i($pos,1)),
  289. nqp::ifnull(
  290. nqp::atpos($reified,$pos), # reified ok
  291. self!AT-POS-CONTAINER($pos) # reifier didn't reach
  292. )
  293. ),
  294. self!AT-POS-CONTAINER($pos) # create an outlander
  295. )
  296. )
  297. ),
  298. # no reified, implies no todo
  299. nqp::stmts( # create reified
  300. nqp::bindattr(self,List,'$!reified',nqp::create(IterationBuffer)),
  301. self!AT-POS-CONTAINER($pos) # create an outlander
  302. )
  303. )
  304. )
  305. }
  306. # because this is a very hot path, we copied the code from the int candidate
  307. multi method AT-POS(Array:D: Int:D $pos) is raw {
  308. nqp::if(
  309. nqp::islt_i($pos,0),
  310. Failure.new(X::OutOfRange.new(
  311. :what($*INDEX // 'Index'),:got($pos),:range<0..^Inf>)),
  312. nqp::if(
  313. (my $reified := nqp::getattr(self,List,'$!reified')).DEFINITE,
  314. nqp::ifnull(
  315. nqp::atpos($reified,$pos), # found it!
  316. nqp::if(
  317. nqp::islt_i(
  318. $pos,nqp::elems(nqp::getattr(self,List,'$!reified'))),
  319. self!AT-POS-CONTAINER($pos), # it's a hole
  320. nqp::if( # too far out, try reifying
  321. (my $todo := nqp::getattr(self,List,'$!todo')).DEFINITE,
  322. nqp::stmts(
  323. $todo.reify-at-least(nqp::add_i($pos,1)),
  324. nqp::ifnull(
  325. nqp::atpos($reified,$pos), # reified ok
  326. self!AT-POS-CONTAINER($pos) # reifier didn't reach
  327. )
  328. ),
  329. self!AT-POS-CONTAINER($pos) # create an outlander
  330. )
  331. )
  332. ),
  333. # no reified, implies no todo
  334. nqp::stmts( # create reified
  335. nqp::bindattr(self,List,'$!reified',nqp::create(IterationBuffer)),
  336. self!AT-POS-CONTAINER($pos) # create an outlander
  337. )
  338. )
  339. )
  340. }
  341. method !AT-POS-CONTAINER(int $pos) is raw {
  342. nqp::p6bindattrinvres(
  343. (my $scalar := nqp::p6scalarfromdesc($!descriptor)),
  344. Scalar,
  345. '$!whence',
  346. -> { nqp::bindpos(nqp::getattr(self,List,'$!reified'),$pos,$scalar) }
  347. )
  348. }
  349. multi method ASSIGN-POS(Array:D: int $pos, Mu \assignee) {
  350. nqp::if(
  351. nqp::islt_i($pos,0),
  352. Failure.new(X::OutOfRange.new(
  353. :what($*INDEX // 'Index'),:got($pos),:range<0..^Inf>)),
  354. nqp::if(
  355. (my $reified := nqp::getattr(self,List,'$!reified')).DEFINITE,
  356. nqp::if(
  357. nqp::existspos($reified,$pos),
  358. (nqp::atpos($reified,$pos) = assignee), # found it!
  359. nqp::if(
  360. nqp::islt_i($pos,nqp::elems($reified)), # it's a hole
  361. (nqp::bindpos($reified,$pos,
  362. nqp::p6scalarfromdesc($!descriptor)) = assignee),
  363. nqp::if(
  364. (my $todo := nqp::getattr(self,List,'$!todo')).DEFINITE,
  365. nqp::stmts( # can reify
  366. $todo.reify-at-least(nqp::add_i($pos,1)),
  367. nqp::if(
  368. nqp::existspos($reified,$pos),
  369. (nqp::atpos($reified,$pos) = assignee), # reified
  370. (nqp::bindpos($reified,$pos, # outlander
  371. nqp::p6scalarfromdesc($!descriptor)) = assignee),
  372. )
  373. ),
  374. (nqp::bindpos($reified,$pos, # outlander
  375. nqp::p6scalarfromdesc($!descriptor)) = assignee)
  376. )
  377. )
  378. ),
  379. nqp::stmts( # new outlander
  380. nqp::bindattr(self,List,'$!reified',nqp::create(IterationBuffer)),
  381. (nqp::bindpos(nqp::getattr(self,List,'$!reified'),$pos,
  382. nqp::p6scalarfromdesc($!descriptor)) = assignee)
  383. )
  384. )
  385. )
  386. }
  387. # because this is a very hot path, we copied the code from the int candidate
  388. multi method ASSIGN-POS(Array:D: Int:D $pos, Mu \assignee) {
  389. nqp::if(
  390. nqp::islt_i($pos,0),
  391. Failure.new(X::OutOfRange.new(
  392. :what($*INDEX // 'Index'),:got($pos),:range<0..^Inf>)),
  393. nqp::if(
  394. (my $reified := nqp::getattr(self,List,'$!reified')).DEFINITE,
  395. nqp::if(
  396. nqp::existspos($reified,$pos),
  397. (nqp::atpos($reified,$pos) = assignee), # found it!
  398. nqp::if(
  399. nqp::islt_i($pos,nqp::elems($reified)), # it's a hole
  400. (nqp::bindpos($reified,$pos,
  401. nqp::p6scalarfromdesc($!descriptor)) = assignee),
  402. nqp::if(
  403. (my $todo := nqp::getattr(self,List,'$!todo')).DEFINITE,
  404. nqp::stmts( # can reify
  405. $todo.reify-at-least(nqp::add_i($pos,1)),
  406. nqp::if(
  407. nqp::existspos($reified,$pos),
  408. (nqp::atpos($reified,$pos) = assignee), # reified
  409. (nqp::bindpos($reified,$pos, # outlander
  410. nqp::p6scalarfromdesc($!descriptor)) = assignee),
  411. )
  412. ),
  413. (nqp::bindpos($reified,$pos, # outlander
  414. nqp::p6scalarfromdesc($!descriptor)) = assignee)
  415. )
  416. )
  417. ),
  418. nqp::stmts( # new outlander
  419. nqp::bindattr(self,List,'$!reified',nqp::create(IterationBuffer)),
  420. (nqp::bindpos(nqp::getattr(self,List,'$!reified'),$pos,
  421. nqp::p6scalarfromdesc($!descriptor)) = assignee)
  422. )
  423. )
  424. )
  425. }
  426. multi method BIND-POS(Array:D: int $pos, Mu \bindval) is raw {
  427. nqp::if(
  428. nqp::islt_i($pos,0),
  429. Failure.new(X::OutOfRange.new(
  430. :what($*INDEX // 'Index'),:got($pos),:range<0..^Inf>)),
  431. nqp::stmts(
  432. nqp::if(
  433. nqp::getattr(self,List,'$!reified').DEFINITE,
  434. nqp::if(
  435. (nqp::isge_i(
  436. $pos,nqp::elems(nqp::getattr(self,List,'$!reified')))
  437. && nqp::getattr(self,List,'$!todo').DEFINITE),
  438. nqp::getattr(self,List,'$!todo').reify-at-least(
  439. nqp::add_i($pos,1)),
  440. ),
  441. nqp::bindattr(self,List,'$!reified',nqp::create(IterationBuffer))
  442. ),
  443. nqp::bindpos(nqp::getattr(self,List,'$!reified'),$pos,bindval)
  444. )
  445. )
  446. }
  447. # because this is a very hot path, we copied the code from the int candidate
  448. multi method BIND-POS(Array:D: Int:D $pos, Mu \bindval) is raw {
  449. nqp::if(
  450. nqp::islt_i($pos,0),
  451. Failure.new(X::OutOfRange.new(
  452. :what($*INDEX // 'Index'),:got($pos),:range<0..^Inf>)),
  453. nqp::stmts(
  454. nqp::if(
  455. nqp::getattr(self,List,'$!reified').DEFINITE,
  456. nqp::if(
  457. (nqp::isge_i(
  458. $pos,nqp::elems(nqp::getattr(self,List,'$!reified')))
  459. && nqp::getattr(self,List,'$!todo').DEFINITE),
  460. nqp::getattr(self,List,'$!todo').reify-at-least(
  461. nqp::add_i($pos,1)),
  462. ),
  463. nqp::bindattr(self,List,'$!reified',nqp::create(IterationBuffer))
  464. ),
  465. nqp::bindpos(nqp::getattr(self,List,'$!reified'),$pos,bindval)
  466. )
  467. )
  468. }
  469. multi method DELETE-POS(Array:D: int $pos) is raw {
  470. nqp::if(
  471. nqp::islt_i($pos,0),
  472. Failure.new(X::OutOfRange.new(
  473. :what($*INDEX // 'Index'),:got($pos),:range<0..^Inf>)),
  474. nqp::if(
  475. (my $reified := nqp::getattr(self,List,'$!reified')).DEFINITE,
  476. nqp::if(
  477. nqp::isle_i( # something to delete
  478. $pos,my int $end = nqp::sub_i(nqp::elems($reified),1)),
  479. nqp::stmts(
  480. (my $value := nqp::ifnull( # save the value
  481. nqp::atpos($reified,$pos),
  482. self.default
  483. )),
  484. nqp::bindpos($reified,$pos,nqp::null), # remove this one
  485. nqp::if(
  486. nqp::iseq_i($pos,$end),
  487. nqp::stmts( # shorten from end
  488. (my int $i = $pos),
  489. nqp::while(
  490. (nqp::isge_i(($i = nqp::sub_i($i,1)),0)
  491. && nqp::not_i(nqp::existspos($reified,$i))),
  492. nqp::null
  493. ),
  494. nqp::setelems($reified,nqp::add_i($i,1))
  495. ),
  496. ),
  497. $value # value, if any
  498. ),
  499. self.default # outlander
  500. ),
  501. self.default # no elements
  502. )
  503. )
  504. }
  505. multi method DELETE-POS(Array:D: Int:D $pos) is raw {
  506. self.DELETE-POS(nqp::unbox_i($pos))
  507. }
  508. # MUST have a separate Slip variant to have it slip
  509. multi method push(Array:D: Slip \value) {
  510. self.is-lazy
  511. ?? X::Cannot::Lazy.new(action => 'push to').throw
  512. !! self!append-list(value)
  513. }
  514. multi method push(Array:D: \value) {
  515. nqp::if(
  516. self.is-lazy,
  517. X::Cannot::Lazy.new(action => 'push to').throw,
  518. nqp::stmts(
  519. nqp::push(
  520. nqp::if(
  521. nqp::getattr(self,List,'$!reified').DEFINITE,
  522. nqp::getattr(self,List,'$!reified'),
  523. nqp::bindattr(self,List,'$!reified',
  524. nqp::create(IterationBuffer))
  525. ),
  526. nqp::assign(nqp::p6scalarfromdesc($!descriptor),value)
  527. ),
  528. self
  529. )
  530. )
  531. }
  532. multi method push(Array:D: **@values is raw) {
  533. self.is-lazy
  534. ?? X::Cannot::Lazy.new(action => 'push to').throw
  535. !! self!append-list(@values)
  536. }
  537. multi method append(Array:D: \value) {
  538. nqp::if(
  539. self.is-lazy,
  540. X::Cannot::Lazy.new(action => 'append to').throw,
  541. nqp::if(
  542. (nqp::iscont(value) || nqp::not_i(nqp::istype(value, Iterable))),
  543. nqp::stmts(
  544. nqp::push(
  545. nqp::if(
  546. nqp::getattr(self,List,'$!reified').DEFINITE,
  547. nqp::getattr(self,List,'$!reified'),
  548. nqp::bindattr(self,List,'$!reified',
  549. nqp::create(IterationBuffer))
  550. ),
  551. nqp::assign(nqp::p6scalarfromdesc($!descriptor),value)
  552. ),
  553. self
  554. ),
  555. self!append-list(value.list)
  556. )
  557. )
  558. }
  559. multi method append(Array:D: **@values is raw) {
  560. self.is-lazy
  561. ?? X::Cannot::Lazy.new(action => 'append to').throw
  562. !! self!append-list(@values)
  563. }
  564. method !append-list(@values) {
  565. nqp::if(
  566. nqp::eqaddr(
  567. @values.iterator.push-until-lazy(
  568. ArrayReificationTarget.new(
  569. nqp::if(
  570. nqp::getattr(self,List,'$!reified').DEFINITE,
  571. nqp::getattr(self,List,'$!reified'),
  572. nqp::bindattr(self,List,'$!reified',
  573. nqp::create(IterationBuffer))
  574. ),
  575. nqp::decont($!descriptor)
  576. )
  577. ),
  578. IterationEnd
  579. ),
  580. self,
  581. X::Cannot::Lazy.new(:action<push>,:what(self.^name)).throw
  582. )
  583. }
  584. multi method unshift(Array:D: Slip \value) {
  585. self!prepend-list(value)
  586. }
  587. multi method unshift(Array:D: \value) {
  588. nqp::stmts(
  589. nqp::unshift(
  590. nqp::if(
  591. nqp::getattr(self,List,'$!reified').DEFINITE,
  592. nqp::getattr(self,List,'$!reified'),
  593. nqp::bindattr(self,List,'$!reified',
  594. nqp::create(IterationBuffer))
  595. ),
  596. nqp::assign(nqp::p6scalarfromdesc($!descriptor),value)
  597. ),
  598. self
  599. )
  600. }
  601. multi method unshift(Array:D: **@values is raw) {
  602. self!prepend-list(@values)
  603. }
  604. multi method prepend(Array:D: \value) {
  605. nqp::if(
  606. (nqp::iscont(value) || nqp::not_i(nqp::istype(value, Iterable))),
  607. nqp::stmts(
  608. nqp::unshift(
  609. nqp::if(
  610. nqp::getattr(self,List,'$!reified').DEFINITE,
  611. nqp::getattr(self,List,'$!reified'),
  612. nqp::bindattr(self,List,'$!reified',
  613. nqp::create(IterationBuffer))
  614. ),
  615. nqp::assign(nqp::p6scalarfromdesc($!descriptor),value)
  616. ),
  617. self
  618. ),
  619. self!prepend-list(value.list)
  620. )
  621. }
  622. multi method prepend(Array:D: **@values is raw) {
  623. self!prepend-list(@values)
  624. }
  625. method !prepend-list(@values) {
  626. nqp::stmts(
  627. nqp::if(
  628. nqp::getattr(self,List,'$!reified').DEFINITE,
  629. nqp::splice(nqp::getattr(self,List,'$!reified'), # prepend existing
  630. nqp::stmts(
  631. @values.iterator.push-all(
  632. ArrayReificationTarget.new(
  633. (my $containers := nqp::create(IterationBuffer)),
  634. nqp::decont($!descriptor)
  635. )
  636. ),
  637. $containers
  638. ),
  639. 0,
  640. 0
  641. ),
  642. @values.iterator.push-all( # no list yet, make this it
  643. ArrayReificationTarget.new(
  644. nqp::bindattr(self,List,'$!reified',
  645. nqp::create(IterationBuffer)),
  646. nqp::decont($!descriptor)
  647. )
  648. )
  649. ),
  650. self
  651. )
  652. }
  653. method pop(Array:D:) is raw is nodal {
  654. nqp::if(
  655. self.is-lazy,
  656. Failure.new(X::Cannot::Lazy.new(action => 'pop from')),
  657. nqp::if(
  658. (nqp::getattr(self,List,'$!reified').DEFINITE
  659. && nqp::elems(nqp::getattr(self,List,'$!reified'))),
  660. nqp::pop(nqp::getattr(self,List,'$!reified')),
  661. Failure.new(X::Cannot::Empty.new(:action<pop>,:what(self.^name)))
  662. )
  663. )
  664. }
  665. method shift(Array:D:) is raw is nodal {
  666. nqp::if(
  667. nqp::getattr(self,List,'$!reified').DEFINITE
  668. && nqp::elems(nqp::getattr(self,List,'$!reified')),
  669. nqp::ifnull( # handle holes
  670. nqp::shift(nqp::getattr(self,List,'$!reified')),
  671. Nil
  672. ),
  673. nqp::if(
  674. (nqp::getattr(self,List,'$!todo').DEFINITE
  675. && nqp::getattr(self,List,'$!todo').reify-at-least(1)),
  676. nqp::shift(nqp::getattr(self,List,'$!reified')),
  677. Failure.new(X::Cannot::Empty.new(:action<shift>,:what(self.^name)))
  678. )
  679. )
  680. }
  681. my $empty := nqp::create(IterationBuffer); # splicing in without values
  682. #------ splice() candidates
  683. multi method splice(Array:D \SELF:) {
  684. nqp::if(
  685. nqp::getattr(SELF,List,'$!reified').DEFINITE,
  686. nqp::stmts(
  687. (my $result := nqp::create(SELF)),
  688. nqp::bindattr($result,Array,'$!descriptor',$!descriptor),
  689. nqp::stmts( # transplant the internals
  690. nqp::bindattr($result,List,'$!reified',
  691. nqp::getattr(SELF,List,'$!reified')),
  692. nqp::if(
  693. nqp::getattr(SELF,List,'$!todo').DEFINITE,
  694. nqp::bindattr($result,List,'$!todo',
  695. nqp::getattr(SELF,List,'$!todo')),
  696. )
  697. ),
  698. (SELF = nqp::create(SELF)), # XXX this preserves $!descriptor ??
  699. $result
  700. ),
  701. nqp::p6bindattrinvres( # nothing to return, so create new one
  702. nqp::create(SELF),Array,'$!descriptor',$!descriptor)
  703. )
  704. }
  705. #------ splice(offset) candidates
  706. multi method splice(Array:D: Whatever $) {
  707. nqp::p6bindattrinvres( # nothing to return, so create new one
  708. nqp::create(self),Array,'$!descriptor',$!descriptor)
  709. }
  710. multi method splice(Array:D: Callable:D $offset) {
  711. self.splice($offset(self.elems))
  712. }
  713. multi method splice(Array:D: Int:D $offset) {
  714. nqp::if(
  715. $offset,
  716. nqp::if(
  717. nqp::islt_i(nqp::unbox_i($offset),0),
  718. self!splice-offset-fail($offset),
  719. nqp::if(
  720. (my $todo := nqp::getattr(self,List,'$!todo')).DEFINITE,
  721. nqp::if(
  722. nqp::isge_i(
  723. $todo.reify-at-least($offset),nqp::unbox_i($offset)),
  724. self!splice-offset(nqp::unbox_i($offset)),
  725. self!splice-offset-fail($offset)
  726. ),
  727. nqp::if(
  728. (nqp::getattr(self,List,'$!reified').DEFINITE
  729. && nqp::isge_i(
  730. nqp::elems(nqp::getattr(self,List,'$!reified')),
  731. nqp::unbox_i($offset))),
  732. self!splice-offset(nqp::unbox_i($offset)),
  733. self!splice-offset-fail($offset)
  734. )
  735. )
  736. ),
  737. self.splice # offset 0, take the quick route out
  738. )
  739. }
  740. method !splice-offset(int $offset) {
  741. nqp::stmts(
  742. (my int $elems = nqp::elems(nqp::getattr(self,List,'$!reified'))),
  743. (my int $size = nqp::sub_i($elems,$offset)),
  744. nqp::bindattr((my $result:= nqp::create(self)),List,'$!reified',
  745. (my $buffer := nqp::setelems(nqp::create(IterationBuffer),$size))),
  746. nqp::bindattr($result,Array,'$!descriptor',$!descriptor),
  747. (my int $i = nqp::sub_i($offset,1)),
  748. nqp::while(
  749. nqp::islt_i(($i = nqp::add_i($i,1)),$elems),
  750. nqp::bindpos($buffer,nqp::sub_i($i,$offset),
  751. nqp::atpos(nqp::getattr(self,List,'$!reified'),$i))
  752. ),
  753. nqp::splice(
  754. nqp::getattr(self,List,'$!reified'),$empty,$offset,$size),
  755. $result
  756. )
  757. }
  758. method !splice-offset-fail($got) {
  759. X::OutOfRange.new(
  760. :what('Offset argument to splice'), :$got, :range("0..{self.elems}")
  761. ).throw
  762. }
  763. #------ splice(offset,size) candidates
  764. multi method splice(Array:D: Whatever $, Whatever $) {
  765. nqp::p6bindattrinvres( # nothing to return, so create new one
  766. nqp::create(self),Array,'$!descriptor',$!descriptor)
  767. }
  768. multi method splice(Array:D: Whatever $, Int:D $size) {
  769. self.splice(self.elems,$size)
  770. }
  771. multi method splice(Array:D: Whatever $, Callable:D $size) {
  772. my int $elems = self.elems;
  773. self.splice($elems,$size(nqp::sub_i($elems,$elems)));
  774. }
  775. multi method splice(Array:D: Callable:D $offset, Callable:D $size) {
  776. nqp::stmts(
  777. (my int $elems = self.elems),
  778. (my int $from = $offset($elems)),
  779. self.splice($from,$size(nqp::sub_i($elems,$from)))
  780. )
  781. }
  782. multi method splice(Array:D: Callable:D $offset, Whatever $) {
  783. self.splice($offset(self.elems))
  784. }
  785. multi method splice(Array:D: Callable:D $offset, Int:D $size) {
  786. self.splice($offset(self.elems),$size)
  787. }
  788. multi method splice(Array:D: Int:D $offset, Whatever $) {
  789. self.splice($offset)
  790. }
  791. multi method splice(Array:D: Int:D $offset, Callable:D $size) {
  792. self.splice($offset,$size(self.elems - $offset))
  793. }
  794. multi method splice(Array:D: Int:D $offset, Int:D $size) {
  795. nqp::if(
  796. nqp::islt_i(nqp::unbox_i($offset),0),
  797. self!splice-offset-fail($offset),
  798. nqp::if(
  799. nqp::islt_i(nqp::unbox_i($size),0),
  800. self!splice-size-fail($size,$offset),
  801. nqp::if(
  802. (my $todo := nqp::getattr(self,List,'$!todo')).DEFINITE,
  803. nqp::if(
  804. nqp::isge_i(
  805. $todo.reify-at-least(
  806. nqp::add_i(nqp::unbox_i($offset),nqp::unbox_i($size))
  807. ),nqp::unbox_i($offset)),
  808. self!splice-offset-size(
  809. nqp::unbox_i($offset),nqp::unbox_i($size)),
  810. self!splice-size-fail($size,$offset)
  811. ),
  812. nqp::if(
  813. nqp::getattr(self,List,'$!reified').DEFINITE,
  814. nqp::if(
  815. nqp::isge_i(
  816. nqp::elems(nqp::getattr(self,List,'$!reified')),
  817. nqp::unbox_i($offset)),
  818. self!splice-offset-size(
  819. nqp::unbox_i($offset),nqp::unbox_i($size)),
  820. self!splice-size-fail($size,$offset)
  821. ),
  822. nqp::if(
  823. nqp::iseq_i(nqp::unbox_i($offset),0),
  824. nqp::p6bindattrinvres( # nothing to return, create new
  825. nqp::create(self),Array,'$!descriptor',$!descriptor),
  826. self!splice-offset-fail($offset)
  827. )
  828. )
  829. )
  830. )
  831. )
  832. }
  833. method !splice-offset-size(int $offset,int $size) {
  834. nqp::stmts(
  835. (my $result := self!splice-save($offset,$size,my int $removed)),
  836. nqp::splice(
  837. nqp::getattr(self,List,'$!reified'),$empty,$offset,$removed),
  838. $result
  839. )
  840. }
  841. method !splice-save(int $offset,int $size, \removed) {
  842. nqp::stmts(
  843. (removed = nqp::if(
  844. nqp::isgt_i(
  845. nqp::add_i($offset,$size),
  846. nqp::elems(nqp::getattr(self,List,'$!reified'))
  847. ),
  848. nqp::sub_i(nqp::elems(nqp::getattr(self,List,'$!reified')),$offset),
  849. $size
  850. )),
  851. nqp::if(
  852. removed,
  853. nqp::stmts(
  854. nqp::bindattr((my $saved:= nqp::create(self)),List,'$!reified',
  855. (my $buffer :=
  856. nqp::setelems(nqp::create(IterationBuffer),removed))),
  857. nqp::bindattr($saved,Array,'$!descriptor',$!descriptor),
  858. (my int $i = -1),
  859. nqp::while(
  860. nqp::islt_i(($i = nqp::add_i($i,1)),removed),
  861. nqp::bindpos($buffer,$i,nqp::atpos(
  862. nqp::getattr(self,List,'$!reified'),nqp::add_i($offset,$i)))
  863. ),
  864. $saved
  865. ),
  866. nqp::p6bindattrinvres( # effective size = 0, create new one
  867. nqp::create(self),Array,'$!descriptor',$!descriptor)
  868. )
  869. )
  870. }
  871. method !splice-size-fail($got,$offset) {
  872. nqp::if(
  873. $offset > self.elems,
  874. self!splice-offset-fail($offset),
  875. X::OutOfRange.new(
  876. :what('Size argument to splice'),
  877. :$got,
  878. :range("0..^{self.elems - $offset}")
  879. ).throw
  880. )
  881. }
  882. #------ splice(offset,size,array) candidates
  883. # we have these 9 multies to avoid infiniloop when incorrect types are
  884. # given to $offset/$size. Other attempts to resolve this showed 30%+
  885. # performance decreases
  886. multi method splice(Array:D: Whatever $offset, Whatever $size, **@new) { self.splice($offset, $size, @new) }
  887. multi method splice(Array:D: Whatever $offset, Callable:D $size, **@new) { self.splice($offset, $size, @new) }
  888. multi method splice(Array:D: Whatever $offset, Int:D $size, **@new) { self.splice($offset, $size, @new) }
  889. multi method splice(Array:D: Callable:D $offset, Whatever $size, **@new) { self.splice($offset, $size, @new) }
  890. multi method splice(Array:D: Callable:D $offset, Callable:D $size, **@new) { self.splice($offset, $size, @new) }
  891. multi method splice(Array:D: Callable:D $offset, Int:D $size, **@new) { self.splice($offset, $size, @new) }
  892. multi method splice(Array:D: Int:D $offset, Whatever $size, **@new) { self.splice($offset, $size, @new) }
  893. multi method splice(Array:D: Int:D $offset, Callable:D $size, **@new) { self.splice($offset, $size, @new) }
  894. multi method splice(Array:D: Int:D $offset, Int:D $size, **@new) { self.splice($offset, $size, @new) }
  895. multi method splice(Array:D: Whatever $, Whatever $, @new) {
  896. self.splice(self.elems,0,@new)
  897. }
  898. multi method splice(Array:D: Whatever $, Int:D $size, @new) {
  899. self.splice(self.elems,$size,@new)
  900. }
  901. multi method splice(Array:D: Whatever $, Callable:D $size, @new) {
  902. my int $elems = self.elems;
  903. self.splice($elems,$size(nqp::sub_i($elems,$elems)),@new);
  904. }
  905. multi method splice(Array:D: Callable:D $offset, Callable:D $size, @new) {
  906. nqp::stmts(
  907. (my int $elems = self.elems),
  908. (my int $from = $offset($elems)),
  909. self.splice($from,$size(nqp::sub_i($elems,$from)),@new)
  910. )
  911. }
  912. multi method splice(Array:D: Callable:D $offset, Whatever $, @new) {
  913. nqp::stmts(
  914. (my int $elems = self.elems),
  915. (my int $from = $offset($elems)),
  916. self.splice($from,nqp::sub_i($elems,$from),@new)
  917. )
  918. }
  919. multi method splice(Array:D: Callable:D $offset, Int:D $size, @new) {
  920. self.splice($offset(self.elems),$size,@new)
  921. }
  922. multi method splice(Array:D: Int:D $offset, Whatever $, @new) {
  923. self.splice($offset,self.elems - $offset,@new)
  924. }
  925. multi method splice(Array:D: Int:D $offset, Callable:D $size, @new) {
  926. self.splice($offset,$size(self.elems - $offset),@new)
  927. }
  928. multi method splice(Array:D: Int:D $offset, Int:D $size, @new) {
  929. nqp::if(
  930. nqp::islt_i(nqp::unbox_i($offset),0),
  931. self!splice-offset-fail($offset),
  932. nqp::if(
  933. nqp::islt_i(nqp::unbox_i($size),0),
  934. self!splice-size-fail($size,$offset),
  935. nqp::if(
  936. (my $todo := nqp::getattr(self,List,'$!todo')).DEFINITE,
  937. nqp::if(
  938. nqp::isge_i(
  939. $todo.reify-at-least(
  940. nqp::add_i(nqp::unbox_i($offset),nqp::unbox_i($size))
  941. ),nqp::unbox_i($offset)),
  942. self!splice-offset-size-new(
  943. nqp::unbox_i($offset),nqp::unbox_i($size),@new),
  944. self!splice-size-fail($size,$offset)
  945. ),
  946. nqp::if(
  947. nqp::isge_i(
  948. nqp::elems(nqp::if(
  949. nqp::getattr(self,List,'$!reified').DEFINITE,
  950. nqp::getattr(self,List,'$!reified'),
  951. nqp::bindattr(self,List,'$!reified',
  952. nqp::create(IterationBuffer))
  953. )),
  954. nqp::unbox_i($offset),
  955. ),
  956. self!splice-offset-size-new(
  957. nqp::unbox_i($offset),nqp::unbox_i($size),@new),
  958. self!splice-offset-fail($offset)
  959. )
  960. )
  961. )
  962. )
  963. }
  964. method !splice-offset-size-new(int $offset,int $size,@new) {
  965. nqp::if(
  966. nqp::eqaddr(@new.iterator.push-until-lazy(
  967. (my $new := IterationBuffer.new)),IterationEnd),
  968. nqp::if( # reified all values to splice in
  969. (nqp::isnull($!descriptor) || nqp::eqaddr(self.of,Mu)),
  970. nqp::stmts( # no typecheck needed
  971. (my $result := self!splice-save($offset,$size,my int $removed)),
  972. nqp::splice(
  973. nqp::getattr(self,List,'$!reified'),$new,$offset,$removed),
  974. $result
  975. ),
  976. nqp::stmts( # typecheck the values first
  977. (my $expected := self.of),
  978. (my int $elems = nqp::elems($new)),
  979. (my int $i = -1),
  980. nqp::while(
  981. (nqp::islt_i(($i = nqp::add_i($i,1)),$elems)
  982. && nqp::istype(nqp::atpos($new,$i),$expected)),
  983. nqp::null
  984. ),
  985. nqp::if(
  986. nqp::islt_i($i,$elems), # exited loop because of wrong type
  987. X::TypeCheck::Splice.new(
  988. :action<splice>,
  989. :got(nqp::atpos($new,$i).WHAT),
  990. :$expected
  991. ).throw,
  992. nqp::stmts(
  993. ($result := self!splice-save($offset,$size,$removed)),
  994. nqp::splice(
  995. nqp::getattr(self,List,'$!reified'),$new,$offset,$removed),
  996. $result
  997. )
  998. )
  999. )
  1000. ),
  1001. X::Cannot::Lazy.new(:action('splice in')).throw
  1002. )
  1003. }
  1004. multi method tail(Array:D: $n) {
  1005. nqp::if(
  1006. nqp::getattr(self,List,'$!todo').DEFINITE,
  1007. self.Any::tail($n),
  1008. Seq.new(
  1009. nqp::if(
  1010. (my $reified := nqp::getattr(self,List,'$!reified')).DEFINITE
  1011. && nqp::elems($reified),
  1012. nqp::stmts(
  1013. (my $iterator := Rakudo::Iterator.ReifiedArray(self)),
  1014. nqp::if(
  1015. nqp::istype($n,Callable)
  1016. && nqp::isgt_i((my $skip := -($n(0).Int)),0),
  1017. $iterator.skip-at-least($skip),
  1018. nqp::unless(
  1019. nqp::istype($n,Whatever) || $n == Inf,
  1020. $iterator.skip-at-least(nqp::elems($reified) - $n)
  1021. )
  1022. ),
  1023. $iterator
  1024. ),
  1025. Rakudo::Iterator.Empty
  1026. )
  1027. )
  1028. )
  1029. }
  1030. # introspection
  1031. method name() {
  1032. nqp::isnull($!descriptor) ?? Nil !! $!descriptor.name
  1033. }
  1034. method of() {
  1035. nqp::isnull($!descriptor) ?? Mu !! $!descriptor.of
  1036. }
  1037. method default() {
  1038. nqp::isnull($!descriptor) ?? Any !! $!descriptor.default
  1039. }
  1040. method dynamic() {
  1041. nqp::isnull($!descriptor) ?? False !! so $!descriptor.dynamic
  1042. }
  1043. multi method perl(Array:D \SELF:) {
  1044. SELF.perlseen('Array', {
  1045. '$' x nqp::iscont(SELF) # self is always deconted
  1046. ~ '['
  1047. ~ self.map({nqp::decont($_).perl}).join(', ')
  1048. ~ ',' x (self.elems == 1 && nqp::istype(self.AT-POS(0),Iterable))
  1049. ~ ']'
  1050. })
  1051. }
  1052. multi method gist(Array:D:) {
  1053. self.gistseen('Array', { '[' ~ self.map({.gist}).join(' ') ~ ']' } )
  1054. }
  1055. multi method WHICH(Array:D:) { self.Mu::WHICH }
  1056. #=============== class Array is closed in src/core/TypedArray.pm ===============