Mercurial > hg > Others > Rakudo
view src/core.c/Array.pm6 @ 0:c341f82e7ad7 default tip
Rakudo branch in cr.ie.u-ryukyu.ac.jp
author | Shinji KONO <kono@ie.u-ryukyu.ac.jp> |
---|---|
date | Thu, 26 Dec 2019 16:50:27 +0900 |
parents | |
children |
line wrap: on
line source
# for our tantrums my class X::TypeCheck { ... }; my class X::TypeCheck::Splice { ... } my class X::Subscript::Negative { ... }; my class X::NotEnoughDimensions { ... }; my class X::Assignment::ArrayShapeMismatch { ... }; # stub what we need now my class array is repr('VMArray') { ... }; # An Array is a List that ensures every item added to it is in a Scalar # container. It also supports push, pop, shift, unshift, splice, BIND-POS, # and so forth. my class Array { # declared in BOOTSTRAP # class Array is List # has Mu $!descriptor; my class ArrayReificationTarget { has $!target; has $!descriptor; method new(\target, Mu \descriptor) { nqp::stmts( nqp::bindattr((my \rt = nqp::create(self)), self,'$!target',target), nqp::p6bindattrinvres(rt, self,'$!descriptor',descriptor) ) } method push(Mu \value --> Nil) { nqp::push($!target, nqp::p6scalarwithvalue($!descriptor, value)); } method append(IterationBuffer:D $buffer --> Nil) { nqp::if( (my int $elems = nqp::elems($buffer)), nqp::stmts( (my int $i = -1), nqp::while( nqp::islt_i(($i = nqp::add_i($i,1)),$elems), nqp::push($!target, nqp::p6scalarwithvalue($!descriptor,nqp::atpos($buffer,$i)) ) ) ) ) } } my class ListReificationTarget { has $!target; method new(\target) { nqp::p6bindattrinvres(nqp::create(self), self, '$!target', target); } method push(Mu \value --> Nil) { nqp::push($!target, nqp::decont(value)); } method append(IterationBuffer:D \buffer --> Nil) { nqp::splice($!target,buffer,nqp::elems($!target),0) } } multi method clone(Array:D: --> Array:D) { nqp::stmts( (my \iter := self.iterator), (my \result := nqp::p6bindattrinvres(nqp::create(self), Array, '$!descriptor', nqp::isnull($!descriptor) ?? (nqp::null) !! nqp::clone($!descriptor))), nqp::if( nqp::eqaddr( IterationEnd, iter.push-until-lazy: my \target := ArrayReificationTarget.new( (my \buffer := nqp::create(IterationBuffer)), nqp::clone($!descriptor))), nqp::p6bindattrinvres(result, List, '$!reified', buffer), nqp::stmts( nqp::bindattr(result, List, '$!reified', buffer), nqp::bindattr((my \todo := nqp::create(List::Reifier)), List::Reifier,'$!current-iter', iter), nqp::bindattr(todo, List::Reifier,'$!reified', buffer), nqp::bindattr(todo, List::Reifier,'$!reification-target', target), nqp::p6bindattrinvres(result, List, '$!todo', todo)))) } my class Todo does Iterator { has int $!i; has $!array; has $!reified; has $!todo; has $!descriptor; method !SET-SELF(\array) { $!i = -1; $!array := array; $!reified := nqp::ifnull( nqp::getattr( array,List,'$!reified'), nqp::bindattr(array,List,'$!reified', nqp::create(IterationBuffer)) ); $!todo := nqp::getattr(array,List, '$!todo'); $!descriptor := nqp::getattr(array,Array,'$!descriptor'); self } method new(\array) { nqp::create(self)!SET-SELF(array) } method pull-one() is raw { nqp::ifnull( nqp::atpos($!reified,$!i = nqp::add_i($!i,1)), nqp::if( nqp::islt_i($!i,nqp::elems($!reified)), self.hole($!i), nqp::if( nqp::isconcrete($!todo), nqp::if( nqp::islt_i( $!i, $!todo.reify-at-least(nqp::add_i($!i,1)) ), nqp::atpos($!reified,$!i), # cannot be nqp::null self.done ), IterationEnd ) ) ) } method hole(int $i) { nqp::p6scalarfromcertaindesc(ContainerDescriptor::BindArrayPos.new( $!descriptor, $!reified, $i)) } method done() is raw { $!todo := nqp::bindattr($!array,List,'$!todo',Mu); IterationEnd } method push-until-lazy(\target) { nqp::if( nqp::isconcrete($!todo), nqp::stmts( (my int $elems = $!todo.reify-until-lazy), (my int $i = $!i), # lexicals faster than attributes nqp::while( # doesn't sink nqp::islt_i($i = nqp::add_i($i,1),$elems), target.push(nqp::atpos($!reified,$i)) ), nqp::if( $!todo.fully-reified, nqp::stmts( ($!i = $i), self.done ), nqp::stmts( ($!i = nqp::sub_i($elems,1)), Mu ) ) ), nqp::stmts( ($elems = nqp::elems($!reified)), ($i = $!i), nqp::while( # doesn't sink nqp::islt_i($i = nqp::add_i($i,1),$elems), target.push( nqp::ifnull(nqp::atpos($!reified,$i),self.hole($i)) ) ), ($!i = $i), IterationEnd ) ) } method is-lazy() { $!todo.DEFINITE && $!todo.is-lazy } } method iterator(Array:D: --> Iterator:D) { nqp::if( nqp::isconcrete(nqp::getattr(self,List,'$!todo')), Todo.new(self), # something to iterate over nqp::if( nqp::isconcrete(nqp::getattr(self,List,'$!reified')), Rakudo::Iterator.ReifiedArray( # everything is already there self, nqp::getattr(self,Array,'$!descriptor') ), Rakudo::Iterator.Empty # nothing now or in the future ) ) } method from-iterator(Array:U: Iterator $iter --> Array:D) { nqp::if( nqp::eqaddr( $iter.push-until-lazy( my \target := ArrayReificationTarget.new( (my \buffer := nqp::create(IterationBuffer)), BEGIN nqp::getcurhllsym('default_cont_spec') ) ), IterationEnd ), nqp::p6bindattrinvres(nqp::create(self),List,'$!reified',buffer), nqp::stmts( nqp::bindattr((my \result := nqp::create(self)), List,'$!reified',buffer), nqp::bindattr((my \todo := nqp::create(List::Reifier)), List::Reifier,'$!current-iter',$iter), nqp::bindattr(todo, List::Reifier,'$!reified',buffer), nqp::bindattr(todo, List::Reifier,'$!reification-target',target), nqp::p6bindattrinvres(result,List,'$!todo',todo) ) ) } method from-list(Array:U: Mu \list --> Array:D) { my \params := nqp::getattr(list,List,'$!reified'); my int $elems = list.elems; # reifies my int $i = -1; my \reified := nqp::create(IterationBuffer); nqp::while( nqp::islt_i(($i = nqp::add_i($i,1)),$elems), nqp::bindpos( reified, $i, nqp::p6scalarwithvalue( (BEGIN nqp::getcurhllsym('default_cont_spec')), nqp::decont(nqp::atpos(params,$i)) ) ) ); nqp::p6bindattrinvres(nqp::create(Array),List,'$!reified',reified) } # handle non-straightforward shapes method !difficult-shape(\shape --> Array:D) { nqp::if( Metamodel::EnumHOW.ACCEPTS(shape.HOW), set-shape(self,shape.^elems), nqp::stmts( warn("Ignoring [{ shape.^name }] as shape specification, did you mean 'my { shape.^name } @foo' ?"), nqp::create(self) ) ) } proto method new(|) {*} multi method new(Array: :$shape! --> Array:D) { nqp::if( nqp::isconcrete($shape), set-shape(self,$shape), self!difficult-shape($shape) ) } multi method new(Array: --> Array:D) { nqp::create(self) } multi method new(Array: \values, :$shape! --> Array:D) { nqp::if( nqp::isconcrete($shape), set-shape(self,$shape), self!difficult-shape($shape) ).STORE(values) } multi method new(Array: \values --> Array:D) { nqp::create(self).STORE(values) } multi method new(Array: **@values is raw, :$shape! --> Array:D) { nqp::if( nqp::isconcrete($shape), set-shape(self,$shape), self!difficult-shape($shape) ).STORE(@values) } multi method new(Array: **@values is raw --> Array:D) { nqp::create(self).STORE(@values) } proto method STORE(Array:D: |) {*} multi method STORE(Array:D: Iterable:D \iterable --> Array:D) { nqp::stmts( (my \buffer = nqp::create(IterationBuffer)), nqp::if( nqp::iscont(iterable), nqp::stmts( # only a single element nqp::push( buffer, nqp::p6scalarwithvalue($!descriptor,iterable) ), nqp::bindattr(self,List,'$!todo',Mu) ), nqp::if( # a real iterator with N elems nqp::eqaddr( (my \iter = iterable.iterator).push-until-lazy( (my \target = ArrayReificationTarget.new( buffer,nqp::decont($!descriptor) )) ), IterationEnd ), nqp::bindattr(self,List,'$!todo',Mu), # exhausted nqp::stmts( # still left to do nqp::bindattr(self,List,'$!todo', my \todo = nqp::create(List::Reifier)), nqp::bindattr(todo,List::Reifier,'$!reified',buffer), nqp::bindattr(todo,List::Reifier,'$!current-iter',iter), nqp::bindattr(todo,List::Reifier,'$!reification-target',target), ) ) ), nqp::p6bindattrinvres(self,List,'$!reified',buffer) ) } multi method STORE(Array:D: Mu \item --> Array:D) { nqp::stmts( nqp::push( (my \buffer = nqp::create(IterationBuffer)), nqp::p6scalarwithvalue($!descriptor, item) ), nqp::bindattr(self,List,'$!todo',Mu), nqp::p6bindattrinvres(self,List,'$!reified',buffer) ) } method reification-target(Array:D: --> ArrayReificationTarget:D) { ArrayReificationTarget.new( nqp::getattr(self, List, '$!reified'), nqp::decont($!descriptor)) } multi method Slip(Array:D: --> Slip:D) { # A Slip-With-Descripto is a special kind of Slip that also has a # descriptor to be able to generate containers for null elements that # have type and default information. my class Slip-With-Descriptor is Slip { has $!descriptor; method iterator() { Rakudo::Iterator.ReifiedArray(self,$!descriptor) } multi method AT-POS(Int:D \pos) { nqp::ifnull( nqp::atpos(nqp::getattr(self,List,'$!reified'),pos), nqp::p6scalarfromcertaindesc(ContainerDescriptor::BindArrayPos.new( $!descriptor, nqp::getattr(self,List,'$!reified'), pos)) ) } method default() { $!descriptor.default } } BEGIN Slip-With-Descriptor.^set_name("Slip"); nqp::if( nqp::isconcrete(nqp::getattr(self,List,'$!todo')), # We're not fully reified, and so have internal mutability still. # The safe thing to do is to take an iterator of ourself and build # the Slip out of that. Slip.from-iterator(self.iterator), # We're fully reified. Make a Slip that shares our reified buffer # but that will fill in default values for nulls. nqp::if( nqp::isconcrete(nqp::getattr(self,List,'$!reified')), nqp::p6bindattrinvres( nqp::p6bindattrinvres( nqp::create(Slip-With-Descriptor), Slip-With-Descriptor, '$!descriptor', $!descriptor ), List, '$!reified', nqp::clone(nqp::getattr(self,List,'$!reified')) ), nqp::create(Slip) ) ) } method FLATTENABLE_LIST() { nqp::if( nqp::isconcrete(nqp::getattr(self,List,'$!todo')), nqp::stmts( nqp::getattr(self,List,'$!todo').reify-all, nqp::getattr(self,List,'$!reified') ), nqp::if( nqp::isconcrete(my $reified := nqp::getattr(self,List,'$!reified')), nqp::stmts( nqp::if( (my int $elems = nqp::elems($reified)), nqp::stmts( (my int $i = -1), nqp::while( nqp::islt_i(($i = nqp::add_i($i,1)),$elems), nqp::if( nqp::isnull(nqp::atpos($reified,$i)), nqp::bindpos( $reified, $i, nqp::p6scalarfromcertaindesc($!descriptor) ) ) ) ) ), nqp::getattr(self,List,'$!reified') ), nqp::bindattr(self,List,'$!reified',nqp::create(IterationBuffer)) ) ) } multi method flat(Array:U:) { self } multi method flat(Array:D:) { Seq.new(self.iterator) } multi method List(Array:D: :$view --> List:D) { nqp::if( self.is-lazy, # can't make a List X::Cannot::Lazy.new(:action<List>).throw, nqp::if( # all reified nqp::isconcrete(my $reified := nqp::getattr(self,List,'$!reified')), nqp::if( $view, # assume no change in array $reified.List, nqp::stmts( # make cow copy (my int $elems = nqp::elems($reified)), (my $cow := nqp::setelems(nqp::create(IterationBuffer),$elems)), (my int $i = -1), nqp::while( nqp::islt_i(($i = nqp::add_i($i,1)),$elems), nqp::bindpos($cow,$i,nqp::ifnull(nqp::decont(nqp::atpos($reified,$i)),Nil)), ), $cow.List ) ), nqp::create(List) # was empty, is empty ) ) } method shape(Array: --> List:D) { (*,) } # should probably be Array:D: multi method AT-POS(Array:D: int $pos) is raw { my $reified := nqp::getattr(self, List, '$!reified'); my $result := nqp::bitand_i(nqp::isge_i($pos, 0), nqp::isconcrete($reified)) ?? nqp::atpos($reified, $pos) !! nqp::null; nqp::ifnull($result, self!AT_POS_SLOW($pos)) } # because this is a very hot path, we copied the code from the int candidate multi method AT-POS(Array:D: Int:D $pos) is raw { my $reified := nqp::getattr(self, List, '$!reified'); my $result := nqp::bitand_i(nqp::isge_i($pos, 0), nqp::isconcrete($reified)) ?? nqp::atpos($reified, $pos) !! nqp::null; nqp::ifnull($result, self!AT_POS_SLOW($pos)) } # handle any lookup that's not simple method !AT_POS_SLOW(int $pos) is raw { nqp::if( nqp::islt_i($pos, 0), self!INDEX_OOR($pos), nqp::if( nqp::isconcrete(my $reified := nqp::getattr(self,List,'$!reified')), nqp::if( nqp::islt_i($pos,nqp::elems($reified)), self!AT_POS_CONTAINER($pos), # it's a hole nqp::if( # too far out, try reifying nqp::isconcrete(my $todo := nqp::getattr(self,List,'$!todo')), nqp::stmts( $todo.reify-at-least(nqp::add_i($pos,1)), nqp::ifnull( nqp::atpos($reified,$pos), # reified ok self!AT_POS_CONTAINER($pos) # reifier didn't reach ) ), self!AT_POS_CONTAINER($pos) # create an outlander ) ), # no reified, implies no todo nqp::stmts( # create reified nqp::bindattr(self,List,'$!reified',nqp::create(IterationBuffer)), self!AT_POS_CONTAINER($pos) # create an outlander ) ) ) } method !AT_POS_CONTAINER(int $pos) is raw { my $desc := $!descriptor; my $scalar := nqp::create(Scalar); nqp::bindattr($scalar, Scalar, '$!value', nqp::isnull($desc) ?? Any !! nqp::getattr($desc, ContainerDescriptor, '$!default')); nqp::bindattr($scalar, Scalar, '$!descriptor', ContainerDescriptor::BindArrayPos.new( $desc, nqp::getattr(self,List,'$!reified'), $pos)); $scalar } multi method ASSIGN-POS(Array:D: int $pos, Mu \assignee) is raw { my \reified := nqp::getattr(self,List,'$!reified'); my \assignee_decont := nqp::decont(assignee); nqp::isge_i($pos, 0) && nqp::isconcrete(reified) && nqp::not_i(nqp::isconcrete(nqp::getattr(self,List,'$!todo'))) ?? nqp::stmts( (nqp::p6assign( nqp::ifnull( nqp::atpos(reified, $pos), nqp::bindpos(reified, $pos, nqp::p6bindattrinvres(nqp::create(Scalar), Scalar, '$!descriptor', $!descriptor))), assignee_decont)), assignee_decont) !! self!ASSIGN_POS_SLOW_PATH($pos, assignee_decont) } # because this is a very hot path, we copied the code from the int candidate multi method ASSIGN-POS(Array:D: Int:D $pos, Mu \assignee) is raw { my \assignee_decont := nqp::decont(assignee); nqp::bitand_i( nqp::bitand_i(nqp::isge_i($pos, 0), nqp::isconcrete(nqp::getattr(self,List,'$!reified'))), nqp::not_i(nqp::isconcrete(nqp::getattr(self,List,'$!todo')))) ?? self!ASSIGN_POS_FAST_PATH($pos, assignee_decont) !! self!ASSIGN_POS_SLOW_PATH($pos, assignee_decont) } method !ASSIGN_POS_FAST_PATH(Array:D: Int:D $pos, Mu \assignee_decont) is raw { my \reified := nqp::getattr(self,List,'$!reified'); my int $ipos = $pos; nqp::stmts( nqp::p6assign( nqp::ifnull( nqp::atpos(reified, $ipos), nqp::bindpos(reified, $ipos, nqp::p6bindattrinvres(nqp::create(Scalar), Scalar, '$!descriptor', $!descriptor))), assignee_decont), assignee_decont) } method !ASSIGN_POS_SLOW_PATH(Array:D: Int:D $pos, Mu \assignee) is raw { nqp::if( nqp::islt_i($pos,0), self!INDEX_OOR($pos), nqp::p6assign(nqp::if( nqp::isconcrete(nqp::getattr(self,List,'$!reified')), nqp::ifnull( nqp::atpos(nqp::getattr(self,List,'$!reified'),$pos), nqp::if( nqp::islt_i( # it's a hole $pos, nqp::elems(nqp::getattr(self,List,'$!reified')) ), nqp::bindpos( nqp::getattr(self,List,'$!reified'), $pos, nqp::p6bindattrinvres(nqp::create(Scalar), Scalar, '$!descriptor', $!descriptor) ), nqp::if( nqp::isconcrete(nqp::getattr(self,List,'$!todo')), nqp::stmts( # can reify nqp::getattr(self,List,'$!todo') .reify-at-least(nqp::add_i($pos,1)), nqp::ifnull( nqp::atpos( # reified nqp::getattr(self,List,'$!reified'), $pos ), nqp::bindpos( # outlander nqp::getattr(self,List,'$!reified'), $pos, nqp::p6bindattrinvres(nqp::create(Scalar), Scalar, '$!descriptor', $!descriptor) ) ) ), nqp::bindpos( # outlander without todo nqp::getattr(self,List,'$!reified'), $pos, nqp::p6bindattrinvres(nqp::create(Scalar), Scalar, '$!descriptor', $!descriptor) ) ) ) ), nqp::bindpos( # new outlander nqp::bindattr(self,List,'$!reified',nqp::create(IterationBuffer)), $pos, nqp::p6bindattrinvres(nqp::create(Scalar), Scalar, '$!descriptor', $!descriptor) ) ), assignee) ) } multi method BIND-POS(Array:D: int $pos, Mu \bindval) is raw { nqp::if( nqp::islt_i($pos,0), self!INDEX_OOR($pos), nqp::stmts( nqp::if( nqp::isconcrete(nqp::getattr(self,List,'$!reified')), nqp::if( nqp::isge_i( $pos, nqp::elems(nqp::getattr(self,List,'$!reified')) ) && nqp::isconcrete(nqp::getattr(self,List,'$!todo')), nqp::getattr(self,List,'$!todo').reify-at-least( nqp::add_i($pos,1)) ), nqp::bindattr(self,List,'$!reified',nqp::create(IterationBuffer)) ), nqp::bindpos(nqp::getattr(self,List,'$!reified'),$pos,bindval) ) ) } # because this is a very hot path, we copied the code from the int candidate multi method BIND-POS(Array:D: Int:D $pos, Mu \bindval) is raw { nqp::if( nqp::islt_i($pos,0), self!INDEX_OOR($pos), nqp::stmts( nqp::if( nqp::isconcrete(nqp::getattr(self,List,'$!reified')), nqp::if( nqp::isge_i( $pos, nqp::elems(nqp::getattr(self,List,'$!reified')) ) && nqp::isconcrete(nqp::getattr(self,List,'$!todo')), nqp::getattr(self,List,'$!todo').reify-at-least( nqp::add_i($pos,1)), ), nqp::bindattr(self,List,'$!reified',nqp::create(IterationBuffer)) ), nqp::bindpos(nqp::getattr(self,List,'$!reified'),$pos,bindval) ) ) } multi method DELETE-POS(Array:D: int $pos) is raw { nqp::if( nqp::islt_i($pos,0), self!INDEX_OOR($pos), nqp::if( nqp::isconcrete(my $reified := nqp::getattr(self,List,'$!reified')), nqp::stmts( nqp::if( nqp::isconcrete(my $todo := nqp::getattr(self,List,'$!todo')), $todo.reify-at-least(nqp::add_i($pos,1)), ), nqp::if( nqp::isle_i( # something to delete $pos,my int $end = nqp::sub_i(nqp::elems($reified),1)), nqp::stmts( (my $value := nqp::ifnull( # save the value nqp::atpos($reified,$pos), self.default )), nqp::bindpos($reified,$pos,nqp::null), # remove this one nqp::if( nqp::iseq_i($pos,$end) && nqp::not_i(nqp::defined($todo)), nqp::stmts( # shorten from end (my int $i = $pos), nqp::while( (nqp::isge_i(($i = nqp::sub_i($i,1)),0) && nqp::not_i(nqp::existspos($reified,$i))), nqp::null ), nqp::setelems($reified,nqp::add_i($i,1)) ), ), $value # value, if any ), self.default # outlander ), ), self.default # no elements ) ) } multi method DELETE-POS(Array:D: Int:D $pos) is raw { self.DELETE-POS(nqp::unbox_i($pos)) } method !INDEX_OOR($pos) { Failure.new(X::OutOfRange.new( :what($*INDEX // 'Index'), :got($pos), :range<0..^Inf> )) } # MUST have a separate Slip variant to have it slip multi method push(Array:D: Slip \value --> Array:D) { self.is-lazy ?? X::Cannot::Lazy.new(action => 'push to').throw !! self!append-list(value) } multi method push(Array:D: \value --> Array:D) { nqp::if( self.is-lazy, X::Cannot::Lazy.new(action => 'push to').throw, nqp::stmts( nqp::push( nqp::if( nqp::isconcrete(nqp::getattr(self,List,'$!reified')), nqp::getattr(self,List,'$!reified'), nqp::bindattr(self,List,'$!reified', nqp::create(IterationBuffer)) ), nqp::p6scalarwithvalue($!descriptor,value) ), self ) ) } multi method push(Array:D: **@values is raw --> Array:D) { self.is-lazy ?? X::Cannot::Lazy.new(action => 'push to').throw !! self!append-list(@values) } multi method append(Array:D: \value --> Array:D) { nqp::if( self.is-lazy, X::Cannot::Lazy.new(action => 'append to').throw, nqp::if( (nqp::iscont(value) || nqp::not_i(nqp::istype(value, Iterable))), nqp::stmts( nqp::push( nqp::if( nqp::isconcrete(nqp::getattr(self,List,'$!reified')), nqp::getattr(self,List,'$!reified'), nqp::bindattr(self,List,'$!reified', nqp::create(IterationBuffer)) ), nqp::p6scalarwithvalue($!descriptor,value) ), self ), self!append-list(value.list) ) ) } multi method append(Array:D: **@values is raw --> Array:D) { self.is-lazy ?? X::Cannot::Lazy.new(action => 'append to').throw !! self!append-list(@values) } method !append-list(Array:D: @values --> Array:D) { nqp::if( nqp::eqaddr( @values.iterator.push-until-lazy( ArrayReificationTarget.new( nqp::if( nqp::isconcrete(nqp::getattr(self,List,'$!reified')), nqp::getattr(self,List,'$!reified'), nqp::bindattr(self,List,'$!reified', nqp::create(IterationBuffer)) ), nqp::decont($!descriptor) ) ), IterationEnd ), self, X::Cannot::Lazy.new(:action<push>,:what(self.^name)).throw ) } multi method unshift(Array:D: Slip \value --> Array:D) { self!prepend-list(value) } multi method unshift(Array:D: \value --> Array:D) { nqp::stmts( nqp::unshift( nqp::if( nqp::isconcrete(nqp::getattr(self,List,'$!reified')), nqp::getattr(self,List,'$!reified'), nqp::bindattr(self,List,'$!reified', nqp::create(IterationBuffer)) ), nqp::p6scalarwithvalue($!descriptor,value) ), self ) } multi method unshift(Array:D: **@values is raw --> Array:D) { self!prepend-list(@values) } multi method prepend(Array:D: \value --> Array:D) { nqp::if( (nqp::iscont(value) || nqp::not_i(nqp::istype(value, Iterable))), nqp::stmts( nqp::unshift( nqp::if( nqp::isconcrete(nqp::getattr(self,List,'$!reified')), nqp::getattr(self,List,'$!reified'), nqp::bindattr(self,List,'$!reified', nqp::create(IterationBuffer)) ), nqp::p6scalarwithvalue($!descriptor,value) ), self ), self!prepend-list(value.list) ) } multi method prepend(Array:D: **@values is raw --> Array:D) { self!prepend-list(@values) } method !prepend-list(Array:D: @values --> Array:D) { nqp::stmts( nqp::if( nqp::isconcrete(nqp::getattr(self,List,'$!reified')), nqp::splice(nqp::getattr(self,List,'$!reified'), # prepend existing nqp::stmts( @values.iterator.push-all( ArrayReificationTarget.new( (my $containers := nqp::create(IterationBuffer)), nqp::decont($!descriptor) ) ), $containers ), 0, 0 ), @values.iterator.push-all( # no list yet, make this it ArrayReificationTarget.new( nqp::bindattr(self,List,'$!reified', nqp::create(IterationBuffer)), nqp::decont($!descriptor) ) ) ), self ) } method pop(Array:D:) is raw is nodal { nqp::if( self.is-lazy, Failure.new(X::Cannot::Lazy.new(action => 'pop from')), nqp::if( nqp::isconcrete(nqp::getattr(self,List,'$!reified')) && nqp::elems(nqp::getattr(self,List,'$!reified')), nqp::pop(nqp::getattr(self,List,'$!reified')), Failure.new(X::Cannot::Empty.new(:action<pop>,:what(self.^name))) ) ) } method shift(Array:D:) is raw is nodal { nqp::if( nqp::isconcrete(nqp::getattr(self,List,'$!reified')) && nqp::elems(nqp::getattr(self,List,'$!reified')), nqp::ifnull( # handle holes nqp::shift(nqp::getattr(self,List,'$!reified')), Nil ), nqp::if( nqp::isconcrete(nqp::getattr(self,List,'$!todo')) && nqp::getattr(self,List,'$!todo').reify-at-least(1), nqp::shift(nqp::getattr(self,List,'$!reified')), Failure.new(X::Cannot::Empty.new(:action<shift>,:what(self.^name))) ) ) } my $empty := nqp::create(IterationBuffer); # splicing in without values #------ splice() candidates multi method splice(Array:D \SELF: --> Array:D) { nqp::if( nqp::isconcrete(nqp::getattr(SELF,List,'$!reified')), nqp::stmts( (my $result := nqp::create(SELF)), nqp::bindattr($result,Array,'$!descriptor',$!descriptor), nqp::stmts( # transplant the internals nqp::bindattr($result,List,'$!reified', nqp::getattr(SELF,List,'$!reified')), nqp::if( nqp::isconcrete(nqp::getattr(SELF,List,'$!todo')), nqp::bindattr($result,List,'$!todo', nqp::getattr(SELF,List,'$!todo')), ) ), (SELF = nqp::create(SELF)), # XXX this preserves $!descriptor ?? $result ), nqp::p6bindattrinvres( # nothing to return, so create new one nqp::create(SELF),Array,'$!descriptor',$!descriptor) ) } #------ splice(offset) candidates multi method splice(Array:D: Whatever $ --> Array:D) { nqp::p6bindattrinvres( # nothing to return, so create new one nqp::create(self),Array,'$!descriptor',$!descriptor) } multi method splice(Array:D: Callable:D $offset --> Array:D) { self.splice($offset(self.elems)) } multi method splice(Array:D: Int:D $offset --> Array:D) { nqp::if( $offset, nqp::if( nqp::islt_i(nqp::unbox_i($offset),0), self!splice-offset-fail($offset), nqp::if( nqp::isconcrete(my $todo := nqp::getattr(self,List,'$!todo')), nqp::if( nqp::isge_i( $todo.reify-at-least($offset),nqp::unbox_i($offset)), self!splice-offset(nqp::unbox_i($offset)), self!splice-offset-fail($offset) ), nqp::if( nqp::isconcrete(nqp::getattr(self,List,'$!reified')) && nqp::isge_i( nqp::elems(nqp::getattr(self,List,'$!reified')), nqp::unbox_i($offset) ), self!splice-offset(nqp::unbox_i($offset)), self!splice-offset-fail($offset) ) ) ), self.splice # offset 0, take the quick route out ) } method !splice-offset(Array:D: int $offset --> Array:D) { nqp::stmts( (my $reified := nqp::getattr(self,List,'$!reified')), (my int $elems = nqp::elems($reified)), (my $result:= nqp::create(self)), nqp::unless( nqp::iseq_i($offset,$elems), nqp::stmts( nqp::bindattr($result,List,'$!reified',nqp::slice($reified,$offset,-1)), nqp::splice( $reified, $empty, $offset, nqp::sub_i(nqp::elems($reified),$offset) ), ) ), nqp::p6bindattrinvres($result,Array,'$!descriptor',$!descriptor) ) } method !splice-offset-fail(Array:D: $got) { X::OutOfRange.new( :what('Offset argument to splice'), :$got, :range("0..{self.elems}") ).throw } #------ splice(offset,size) candidates multi method splice(Array:D: Whatever $, Whatever $ --> Array:D) { nqp::p6bindattrinvres( # nothing to return, so create new one nqp::create(self),Array,'$!descriptor',$!descriptor) } multi method splice(Array:D: Whatever $, Int:D $size --> Array:D) { self.splice(self.elems,$size) } multi method splice(Array:D: Whatever $, Callable:D $size --> Array:D) { my int $elems = self.elems; self.splice($elems,$size(nqp::sub_i($elems,$elems))); } multi method splice(Array:D: Callable:D $offset, Callable:D $size --> Array:D) { nqp::stmts( (my int $elems = self.elems), (my int $from = $offset($elems)), self.splice($from,$size(nqp::sub_i($elems,$from))) ) } multi method splice(Array:D: Callable:D $offset, Whatever $ --> Array:D) { self.splice($offset(self.elems)) } multi method splice(Array:D: Callable:D $offset, Int:D $size --> Array:D) { self.splice($offset(self.elems),$size) } multi method splice(Array:D: Int:D $offset, Whatever $ --> Array:D) { self.splice($offset) } multi method splice(Array:D: Int:D $offset, Callable:D $size --> Array:D) { self.splice($offset,$size(self.elems - $offset)) } multi method splice(Array:D: Int:D $offset, Int:D $size --> Array:D) { nqp::if( nqp::islt_i(nqp::unbox_i($offset),0), self!splice-offset-fail($offset), nqp::if( nqp::islt_i(nqp::unbox_i($size),0), self!splice-size-fail($size,$offset), nqp::if( nqp::isconcrete(my $todo := nqp::getattr(self,List,'$!todo')), nqp::if( nqp::isge_i( $todo.reify-at-least( nqp::add_i(nqp::unbox_i($offset),nqp::unbox_i($size)) ),nqp::unbox_i($offset)), self!splice-offset-size( nqp::unbox_i($offset),nqp::unbox_i($size)), self!splice-size-fail($size,$offset) ), nqp::if( nqp::isconcrete(nqp::getattr(self,List,'$!reified')), nqp::if( nqp::isge_i( nqp::elems(nqp::getattr(self,List,'$!reified')), nqp::unbox_i($offset)), self!splice-offset-size( nqp::unbox_i($offset),nqp::unbox_i($size)), self!splice-size-fail($size,$offset) ), nqp::if( nqp::iseq_i(nqp::unbox_i($offset),0), nqp::p6bindattrinvres( # nothing to return, create new nqp::create(self),Array,'$!descriptor',$!descriptor), self!splice-offset-fail($offset) ) ) ) ) ) } method !splice-offset-size(Array:D: int $offset,int $size --> Array:D) { nqp::stmts( (my $result := self!splice-save($offset,$size,my int $removed)), nqp::splice( nqp::getattr(self,List,'$!reified'),$empty,$offset,$removed), $result ) } method !splice-save(Array:D: int $offset,int $size, \removed --> Array:D) { nqp::stmts( (my $reified := nqp::getattr(self,List,'$!reified')), (my $result:= nqp::create(self)), nqp::if( (removed = nqp::if( nqp::isgt_i(nqp::add_i($offset,$size),nqp::elems($reified)), nqp::sub_i(nqp::elems($reified),$offset), $size )), nqp::bindattr( $result, List, '$!reified', nqp::slice($reified,$offset,nqp::sub_i(nqp::add_i($offset,removed),1)) ) ), nqp::p6bindattrinvres($result,Array,'$!descriptor',$!descriptor) ) } method !splice-size-fail(Array:D: $got,$offset) { nqp::if( $offset > self.elems, self!splice-offset-fail($offset), X::OutOfRange.new( :what('Size argument to splice'), :$got, :range("0..^{self.elems - $offset}") ).throw ) } #------ splice(offset,size,array) candidates # we have these 9 multies to avoid infiniloop when incorrect types are # given to $offset/$size. Other attempts to resolve this showed 30%+ # performance decreases multi method splice(Array:D: Whatever $offset, Whatever $size, **@new --> Array:D) { self.splice($offset, $size, @new) } multi method splice(Array:D: Whatever $offset, Callable:D $size, **@new --> Array:D) { self.splice($offset, $size, @new) } multi method splice(Array:D: Whatever $offset, Int:D $size, **@new --> Array:D) { self.splice($offset, $size, @new) } multi method splice(Array:D: Callable:D $offset, Whatever $size, **@new --> Array:D) { self.splice($offset, $size, @new) } multi method splice(Array:D: Callable:D $offset, Callable:D $size, **@new --> Array:D) { self.splice($offset, $size, @new) } multi method splice(Array:D: Callable:D $offset, Int:D $size, **@new --> Array:D) { self.splice($offset, $size, @new) } multi method splice(Array:D: Int:D $offset, Whatever $size, **@new --> Array:D) { self.splice($offset, $size, @new) } multi method splice(Array:D: Int:D $offset, Callable:D $size, **@new --> Array:D) { self.splice($offset, $size, @new) } multi method splice(Array:D: Int:D $offset, Int:D $size, **@new --> Array:D) { self.splice($offset, $size, @new) } multi method splice(Array:D: Whatever $, Whatever $, @new --> Array:D) { self.splice(self.elems,0,@new) } multi method splice(Array:D: Whatever $, Int:D $size, @new --> Array:D) { self.splice(self.elems,$size,@new) } multi method splice(Array:D: Whatever $, Callable:D $size, @new --> Array:D) { my int $elems = self.elems; self.splice($elems,$size(nqp::sub_i($elems,$elems)),@new); } multi method splice(Array:D: Callable:D $offset, Callable:D $size, @new --> Array:D) { nqp::stmts( (my int $elems = self.elems), (my int $from = $offset($elems)), self.splice($from,$size(nqp::sub_i($elems,$from)),@new) ) } multi method splice(Array:D: Callable:D $offset, Whatever $, @new --> Array:D) { nqp::stmts( (my int $elems = self.elems), (my int $from = $offset($elems)), self.splice($from,nqp::sub_i($elems,$from),@new) ) } multi method splice(Array:D: Callable:D $offset, Int:D $size, @new --> Array:D) { self.splice($offset(self.elems),$size,@new) } multi method splice(Array:D: Int:D $offset, Whatever $, @new --> Array:D) { self.splice($offset,self.elems - $offset,@new) } multi method splice(Array:D: Int:D $offset, Callable:D $size, @new --> Array:D) { self.splice($offset,$size(self.elems - $offset),@new) } multi method splice(Array:D: Int:D $offset, Int:D $size, @new --> Array:D) { nqp::if( nqp::islt_i(nqp::unbox_i($offset),0), self!splice-offset-fail($offset), nqp::if( nqp::islt_i(nqp::unbox_i($size),0), self!splice-size-fail($size,$offset), nqp::if( nqp::isconcrete(my $todo := nqp::getattr(self,List,'$!todo')), nqp::if( nqp::isge_i( $todo.reify-at-least( nqp::add_i(nqp::unbox_i($offset),nqp::unbox_i($size)) ),nqp::unbox_i($offset)), self!splice-offset-size-new( nqp::unbox_i($offset),nqp::unbox_i($size),@new), self!splice-size-fail($size,$offset) ), nqp::if( nqp::isge_i( nqp::elems(nqp::if( nqp::isconcrete(nqp::getattr(self,List,'$!reified')), nqp::getattr(self,List,'$!reified'), nqp::bindattr(self,List,'$!reified', nqp::create(IterationBuffer)) )), nqp::unbox_i($offset), ), self!splice-offset-size-new( nqp::unbox_i($offset),nqp::unbox_i($size),@new), self!splice-offset-fail($offset) ) ) ) ) } method !splice-offset-size-new(Array:D: int $offset,int $size,@new --> Array:D) { nqp::if( nqp::eqaddr(@new.iterator.push-until-lazy( (my $new := nqp::create(IterationBuffer))),IterationEnd), nqp::if( # reified all values to splice in (nqp::isnull($!descriptor) || nqp::eqaddr(self.of,Mu)), nqp::stmts( # no typecheck needed (my $result := self!splice-save($offset,$size,my int $removed)), nqp::splice( nqp::getattr(self,List,'$!reified'),$new,$offset,$removed), $result ), nqp::stmts( # typecheck the values first (my $expected := self.of), (my int $elems = nqp::elems($new)), (my int $i = -1), nqp::while( (nqp::islt_i(($i = nqp::add_i($i,1)),$elems) && nqp::istype(nqp::atpos($new,$i),$expected)), nqp::null ), nqp::if( nqp::islt_i($i,$elems), # exited loop because of wrong type X::TypeCheck::Splice.new( :action<splice>, :got(nqp::atpos($new,$i).WHAT), :$expected ).throw, nqp::stmts( ($result := self!splice-save($offset,$size,$removed)), nqp::splice( nqp::getattr(self,List,'$!reified'),$new,$offset,$removed), $result ) ) ) ), X::Cannot::Lazy.new(:action('splice in')).throw ) } multi method tail(Array:D: $n) { nqp::if( nqp::isconcrete(nqp::getattr(self,List,'$!todo')), self.Any::tail($n), Seq.new( nqp::if( nqp::isconcrete( my $reified := nqp::getattr(self,List,'$!reified') ) && nqp::elems($reified), nqp::stmts( (my $iterator := Rakudo::Iterator.ReifiedArray( self, nqp::getattr(self,Array,'$!descriptor') )), nqp::if( nqp::istype($n,Callable) && nqp::isgt_i((my $skip := -($n(0).Int)),0), $iterator.skip-at-least($skip), nqp::unless( nqp::istype($n,Whatever) || $n == Inf, $iterator.skip-at-least(nqp::elems($reified) - $n) ) ), $iterator ), Rakudo::Iterator.Empty ) ) ) } proto method grab(|) {*} multi method grab(Array:D:) { nqp::if( self.is-lazy, X::Cannot::Lazy.new(:action('.grab from')).throw, # can't make a List nqp::if( self.elems, # reifies self.GRAB_ONE, Nil ) ) } multi method grab(Array:D: Callable:D $calculate) { self.grab($calculate(self.elems)) } multi method grab(Array:D: Whatever --> Seq:D) { self.grab(Inf) } my class GrabN does Iterator { has $!array; has int $!count; method !SET-SELF(\array,\count) { nqp::stmts( (my int $elems = nqp::elems(nqp::getattr(array,List,'$!reified'))), ($!array := array), nqp::if( count == Inf, ($!count = $elems), nqp::if( nqp::isgt_i(($!count = count.Int),$elems), ($!count = $elems) ) ), self ) } method new(\a,\c) { nqp::create(self)!SET-SELF(a,c) } method pull-one() { nqp::if( $!count && nqp::elems(nqp::getattr($!array,List,'$!reified')), nqp::stmts( ($!count = nqp::sub_i($!count,1)), $!array.GRAB_ONE ), IterationEnd ) } } multi method grab(Array:D: \count --> Seq:D) { Seq.new( self.elems # reifies ?? GrabN.new(self,count) !! Rakudo::Iterator.Empty ) } method GRAB_ONE(Array:D:) { nqp::stmts( (my $reified := nqp::getattr(self,List,'$!reified')), (my $value := nqp::atpos( $reified, (my int $pos = nqp::floor_n(nqp::rand_n(nqp::elems($reified)))), )), nqp::splice($reified,$empty,$pos,1), $value ) } # introspection method name() { nqp::isnull($!descriptor) ?? Nil !! $!descriptor.name } method of() { nqp::isnull($!descriptor) ?? Mu !! $!descriptor.of } method default() { nqp::isnull($!descriptor) ?? Any !! $!descriptor.default } method dynamic() { nqp::isnull($!descriptor) ?? False !! so $!descriptor.dynamic } multi method perl(Array:D \SELF: --> Str:D) { SELF.perlseen('Array', { '$' x nqp::iscont(SELF) # self is always deconted ~ '[' ~ self.map({nqp::decont($_).perl}).join(', ') ~ ',' x (self.elems == 1 && nqp::istype(self.AT-POS(0),Iterable)) ~ ']' }) } multi method WHICH(Array:D: --> ObjAt:D) { self.Mu::WHICH } #=============== class Array is closed in src/core.c/TypedArray.pm6 ============ # vim: ft=perl6 expandtab sw=4