mirror of
				https://github.com/KevinMidboe/linguist.git
				synced 2025-10-29 17:50:22 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			700 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			Raku
		
	
	
	
	
	
			
		
		
	
	
			700 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			Raku
		
	
	
	
	
	
| # for our tantrums
 | |
| my class X::TypeCheck { ... }
 | |
| my role Supply { ... }
 | |
| 
 | |
| my sub combinations($n, $k) {
 | |
|     my @result;
 | |
|     my @stack;
 | |
| 
 | |
|     return ([],) unless $k;
 | |
| 
 | |
|     @stack.push(0);
 | |
|     gather while @stack {
 | |
|         my $index = @stack - 1;
 | |
|         my $value = @stack.pop;
 | |
| 
 | |
|         while $value < $n {
 | |
|             @result[$index++] = $value++;
 | |
|             @stack.push($value);
 | |
|             if $index == $k {
 | |
|                 take [@result];
 | |
|                 $value = $n;  # fake a last
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| my sub permutations(Int $n) {
 | |
|     $n == 1 ?? ( [0,] ) !!
 | |
|     gather for ^$n -> $i {
 | |
|         my @i = grep none($i), ^$n;
 | |
|         take [$i, @i[@$_]] for permutations($n - 1);
 | |
|     }
 | |
| }
 | |
| 
 | |
| my class List does Positional { # declared in BOOTSTRAP
 | |
|     # class List is Iterable is Cool
 | |
|     #   has Mu $!items;        # VM's array of our reified elements
 | |
|     #   has Mu $!flattens;     # true if this list flattens its parcels
 | |
|     #   has Mu $!nextiter;     # iterator for generating remaining elements
 | |
| 
 | |
|     method new(|) {
 | |
|         my Mu $args := nqp::p6argvmarray();
 | |
|         nqp::shift($args);
 | |
| 
 | |
|         nqp::p6list($args, self.WHAT, Mu);
 | |
|     }
 | |
| 
 | |
|     multi method Bool(List:D:)    { self.gimme(1).Bool }
 | |
|     multi method Int(List:D:)     { self.elems }
 | |
|     multi method end(List:D:)     { self.elems - 1 }
 | |
|     multi method Numeric(List:D:) { self.elems }
 | |
|     multi method Str(List:D:)     { self.join(' ') }
 | |
| 
 | |
|     # Pretend we're a Match assuming we're a list of Matches
 | |
|     method to()         { self.elems ?? self[self.end].to !! Nil }
 | |
|     method from()       { self.elems ?? self[0].from !! Nil }
 | |
| 
 | |
|     method fmt($format = '%s', $separator = ' ') {
 | |
|         self.map({ .fmt($format) }).join($separator);
 | |
|     }
 | |
| 
 | |
|     method flat() { self.flattens
 | |
|                     ?? self
 | |
|                     !! nqp::p6list(nqp::list(self), List, Bool::True)
 | |
|     }
 | |
|     method list() { self }
 | |
|     method lol() {
 | |
|         self.gimme(0);
 | |
|         my Mu $rpa := nqp::clone($!items);
 | |
|         nqp::push($rpa, $!nextiter) if $!nextiter.defined;
 | |
|         nqp::p6list($rpa, LoL, Mu);
 | |
|     }
 | |
| 
 | |
|     method flattens() { $!flattens }
 | |
| 
 | |
|     method Capture() {
 | |
|         self.gimme(*);
 | |
|         my $cap := nqp::create(Capture);
 | |
|         nqp::bindattr($cap, Capture, '$!list', $!items);
 | |
|         $cap
 | |
|     }
 | |
| 
 | |
|     method Parcel() {
 | |
|         my Mu $rpa := nqp::clone(nqp::p6listitems(self));
 | |
|         nqp::push($rpa, $!nextiter) if $!nextiter.defined;
 | |
|         nqp::p6parcel($rpa, Any);
 | |
|     }
 | |
| 
 | |
|     method Supply(List:D:) { Supply.from-list(self) }
 | |
| 
 | |
|     multi method at_pos(List:D: int \pos) is rw {
 | |
|         fail X::OutOfRange.new(:what<Index>,:got(pos),:range<0..Inf>)
 | |
|           if nqp::islt_i(pos,0);
 | |
|         self.exists_pos(pos) ?? nqp::atpos($!items,pos) !! Nil;
 | |
|     }
 | |
|     multi method at_pos(List:D: Int:D \pos) is rw {
 | |
|         my int $pos = nqp::unbox_i(pos);
 | |
|         fail X::OutOfRange.new(:what<Index>,:got(pos),:range<0..Inf>)
 | |
|           if nqp::islt_i($pos,0);
 | |
|         self.exists_pos($pos) ?? nqp::atpos($!items,$pos) !! Nil;
 | |
|     }
 | |
| 
 | |
|     method eager() { self.gimme(*); self }
 | |
| 
 | |
|     method elems() {
 | |
|         return 0 unless self.DEFINITE;
 | |
|         return nqp::elems(nqp::p6listitems(self)) unless nqp::defined($!nextiter);
 | |
|         # Get as many elements as we can.  If gimme stops before
 | |
|         # reaching the end of the list, assume the list is infinite.
 | |
|         my $n := self.gimme(*);
 | |
|         nqp::defined($!nextiter) ?? Inf !! $n
 | |
|     }
 | |
| 
 | |
|     multi method exists_pos(List:D: int $pos) {
 | |
|         return False if nqp::islt_i($pos,0);
 | |
|         self.gimme($pos + 1);
 | |
|         nqp::p6bool(
 | |
|           nqp::not_i(nqp::isnull(nqp::atpos($!items,$pos)))
 | |
|         );
 | |
|     }
 | |
|     multi method exists_pos(List:D: Int:D $pos) {
 | |
|         return False if $pos < 0;
 | |
|         self.gimme($pos + 1);
 | |
|         nqp::p6bool(
 | |
|           nqp::not_i(nqp::isnull(nqp::atpos($!items,nqp::unbox_i($pos))))
 | |
|         );
 | |
|     }
 | |
| 
 | |
|     method gimme($n, :$sink) {
 | |
|         return unless self.DEFINITE;
 | |
|         # loop through iterators until we have at least $n elements
 | |
|         my int $count = nqp::elems(nqp::p6listitems(self));
 | |
|         if nqp::istype($n, Whatever) || nqp::istype($n, Num) && nqp::istrue($n == Inf) {
 | |
|             while $!nextiter.DEFINITE && !$!nextiter.infinite {
 | |
|                 $!nextiter.reify(*, :$sink);
 | |
|                 $count = nqp::elems($!items);
 | |
|             }
 | |
|         }
 | |
|         else {
 | |
|             my int $target = $n.Int;
 | |
|             while nqp::isconcrete($!nextiter) && $count < $target {
 | |
|                 $!nextiter.reify($target - $count, :$sink);
 | |
|                 $count = nqp::elems($!items);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         # return the number of elements we have now
 | |
|         $count
 | |
|     }
 | |
| 
 | |
|     multi method infinite(List:D:) { $!nextiter.infinite }
 | |
| 
 | |
|     method iterator() {
 | |
|         # Return a reified ListIter containing our currently reified elements
 | |
|         # and any subsequent iterator.
 | |
|         my $iter := nqp::create(ListIter);
 | |
|         nqp::bindattr($iter, ListIter, '$!nextiter', $!nextiter);
 | |
|         nqp::bindattr($iter, ListIter, '$!reified', self.Parcel());
 | |
|         $iter;
 | |
|     }
 | |
| 
 | |
|     method munch($n is copy) {
 | |
|         $n = 0 if $n < 0;
 | |
|         $n = self.gimme($n) if nqp::not_i(nqp::istype($n, Int))
 | |
|                                || nqp::not_i(nqp::islist($!items))
 | |
|                                || nqp::islt_i(nqp::elems($!items), nqp::unbox_i($n));
 | |
|         nqp::p6parcel(
 | |
|             nqp::p6shiftpush(nqp::list(), $!items, nqp::unbox_i($n)),
 | |
|             Any
 | |
|         )
 | |
|     }
 | |
| 
 | |
|     proto method pick(|) { * }
 | |
|     multi method pick() {
 | |
|         fail "Cannot .pick from infinite list" if self.infinite;
 | |
|         my $elems = self.elems;
 | |
|         $elems ?? self.at_pos($elems.rand.floor) !! Nil;
 | |
|     }
 | |
|     multi method pick($n is copy) {
 | |
|         fail "Cannot .pick from infinite list" if self.infinite;
 | |
|         ## We use a version of Fisher-Yates shuffle here to
 | |
|         ## replace picked elements with elements from the end
 | |
|         ## of the list, resulting in an O(n) algorithm.
 | |
|         my $elems = self.elems;
 | |
|         return unless $elems;
 | |
|         $n = Inf if nqp::istype($n, Whatever);
 | |
|         $n = $elems if $n > $elems;
 | |
|         return self.at_pos($elems.rand.floor) if $n == 1;
 | |
|         my Mu $rpa := nqp::clone($!items);
 | |
|         my $i;
 | |
|         my Mu $v;
 | |
|         gather while $n > 0 {
 | |
|             $i = nqp::rand_I(nqp::decont($elems), Int);
 | |
|             $elems--; $n--;
 | |
|             $v := nqp::atpos($rpa, nqp::unbox_i($i));
 | |
|             # replace selected element with last unpicked one
 | |
|             nqp::bindpos($rpa, nqp::unbox_i($i),
 | |
|                          nqp::atpos($rpa, nqp::unbox_i($elems)));
 | |
|             take-rw $v;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     method pop() is parcel {
 | |
|         my $elems = self.gimme(*);
 | |
|         fail 'Cannot .pop from an infinite list' if $!nextiter.defined;
 | |
|         $elems > 0
 | |
|           ?? nqp::pop($!items)
 | |
|           !! fail 'Element popped from empty list';
 | |
|     }
 | |
| 
 | |
|     method shift() is parcel {
 | |
|         # make sure we have at least one item, then shift+return it
 | |
|         nqp::islist($!items) && nqp::existspos($!items, 0) || self.gimme(1)
 | |
|           ?? nqp::shift($!items)
 | |
|           !! fail 'Element shifted from empty list';
 | |
|     }
 | |
| 
 | |
|     my &list_push = multi method push(List:D: *@values) {
 | |
|         fail 'Cannot .push an infinite list' if @values.infinite;
 | |
|         nqp::p6listitems(self);
 | |
|         my $elems = self.gimme(*);
 | |
|         fail 'Cannot .push to an infinite list' if $!nextiter.DEFINITE;
 | |
| 
 | |
|         # push is always eager
 | |
|         @values.gimme(*);
 | |
| 
 | |
|         # need type checks?
 | |
|         my $of := self.of;
 | |
| 
 | |
|         unless $of =:= Mu {
 | |
|             X::TypeCheck.new(
 | |
|               operation => '.push',
 | |
|               expected  => $of,
 | |
|               got       => $_,
 | |
|             ).throw unless nqp::istype($_, $of) for @values;
 | |
|         }
 | |
| 
 | |
|         nqp::splice($!items,
 | |
|                 nqp::getattr(@values, List, '$!items'),
 | |
|                 $elems, 0);
 | |
| 
 | |
|         self;
 | |
|     }
 | |
| 
 | |
|     multi method push(List:D: \value) {
 | |
|         if nqp::iscont(value) || nqp::not_i(nqp::istype(value, Iterable)) && nqp::not_i(nqp::istype(value, Parcel)) {
 | |
|             $!nextiter.DEFINITE && self.gimme(*);
 | |
|             fail 'Cannot .push to an infinite list' if $!nextiter.DEFINITE;
 | |
|             nqp::p6listitems(self);
 | |
|             nqp::istype(value, self.of)
 | |
|                 ?? nqp::push($!items, nqp::assign(nqp::p6scalarfromdesc(nqp::null), value))
 | |
|                 !! X::TypeCheck.new(
 | |
|                       operation => '.push',
 | |
|                       expected  => self.of,
 | |
|                       got       => value,
 | |
|                     ).throw;
 | |
|             self
 | |
|         }
 | |
|         else {
 | |
|             list_push(self, value)
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     multi method unshift(List:D: \value) {
 | |
|         if nqp::iscont(value) || !(nqp::istype(value, Iterable) || nqp::istype(value, Parcel)) {
 | |
|             nqp::p6listitems(self);
 | |
|             value.gimme(*) if nqp::istype(value, List); # fixes #121994
 | |
|             nqp::istype(value, self.of)
 | |
|                 ?? nqp::unshift($!items, my $ = value)
 | |
|                 !! X::TypeCheck.new(
 | |
|                       operation => '.push',
 | |
|                       expected  => self.of,
 | |
|                       got       => value,
 | |
|                     ).throw;
 | |
|             self
 | |
|         }
 | |
|         else {
 | |
|             callsame();
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     multi method unshift(List:D: *@values) {
 | |
|         fail 'Cannot .unshift an infinite list' if @values.infinite;
 | |
|         nqp::p6listitems(self);
 | |
| 
 | |
|         # don't bother with type checks
 | |
|         my $of := self.of;
 | |
|         if ( $of =:= Mu ) {
 | |
|             nqp::unshift($!items, @values.pop) while @values;
 | |
|         }
 | |
| 
 | |
|         # we must check types
 | |
|         else {
 | |
|             while @values {
 | |
|                 my $value := @values.pop;
 | |
|                 if nqp::istype($value, $of) {
 | |
|                     nqp::unshift($!items, $value);
 | |
|                 }
 | |
| 
 | |
|                 # huh?
 | |
|                 else {
 | |
|                     X::TypeCheck.new(
 | |
|                       operation => '.unshift',
 | |
|                       expected  => $of,
 | |
|                       got       => $value,
 | |
|                     ).throw;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         self
 | |
|     }
 | |
| 
 | |
|     method plan(List:D: |args) {
 | |
|         nqp::p6listitems(self);
 | |
|         my $elems = self.gimme(*);
 | |
|         fail 'Cannot add plan to an infinite list' if $!nextiter.defined;
 | |
| 
 | |
| #        # need type checks?
 | |
| #        my $of := self.of;
 | |
| #
 | |
| #        unless $of =:= Mu {
 | |
| #            X::TypeCheck.new(
 | |
| #              operation => '.push',
 | |
| #              expected  => $of,
 | |
| #              got       => $_,
 | |
| #            ).throw unless nqp::istype($_, $of) for @values;
 | |
| #        }
 | |
| 
 | |
|         nqp::bindattr(self, List, '$!nextiter', nqp::p6listiter(nqp::list(args.list), self));
 | |
|         Nil;
 | |
|     }
 | |
| 
 | |
|     proto method roll(|) { * }
 | |
|     multi method roll() {
 | |
|         fail "Cannot .roll from infinite list" if self.infinite;
 | |
|         my $elems = self.elems;
 | |
|         $elems ?? self.at_pos($elems.rand.floor) !! Nil;
 | |
|     }
 | |
|     multi method roll($n is copy) {
 | |
|         fail "Cannot .roll from infinite list" if self.infinite;
 | |
|         my $elems = self.elems;
 | |
|         return unless $elems;
 | |
|         $n = Inf if nqp::istype($n, Whatever);
 | |
|         return self.at_pos($elems.rand.floor) if $n == 1;
 | |
| 
 | |
|         gather while $n > 0 {
 | |
|             take nqp::atpos($!items, nqp::unbox_i($elems.rand.floor.Int));
 | |
|             $n--;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     method reverse() {
 | |
|         self.gimme(*);
 | |
|         fail 'Cannot .reverse from an infinite list' if $!nextiter.defined;
 | |
|         my Mu $rev  := nqp::list();
 | |
|         my Mu $orig := nqp::clone($!items);
 | |
|         nqp::push($rev, nqp::pop($orig)) while $orig;
 | |
|         my $rlist := nqp::create(self.WHAT);
 | |
|         nqp::bindattr($rlist, List, '$!items', $rev);
 | |
|         $rlist;
 | |
|     }
 | |
| 
 | |
|     method rotate(Int $n is copy = 1) {
 | |
|         self.gimme(*);
 | |
|         fail 'Cannot .rotate an infinite list' if $!nextiter.defined;
 | |
|         my $items = nqp::p6box_i(nqp::elems($!items));
 | |
|         return self if !$items;
 | |
| 
 | |
|         $n %= $items;
 | |
|         return self if $n == 0;
 | |
| 
 | |
|         my Mu $res := nqp::clone($!items);
 | |
|         if $n > 0 {
 | |
|             nqp::push($res, nqp::shift($res)) while $n--;
 | |
|         }
 | |
|         elsif $n < 0 {
 | |
|             nqp::unshift($res, nqp::pop($res)) while $n++;
 | |
|         }
 | |
|         my $rlist := nqp::create(self.WHAT);
 | |
|         nqp::bindattr($rlist, List, '$!items', $res);
 | |
|         $rlist;
 | |
|     }
 | |
| 
 | |
|     method splice($offset = 0, $size?, *@values) {
 | |
|         self.gimme(*);
 | |
|         my $o = $offset;
 | |
|         my $s = $size;
 | |
|         my $elems = self.elems;
 | |
|         $o = $o($elems) if nqp::istype($o, Callable);
 | |
|         X::OutOfRange.new(
 | |
|             what => 'offset argument to List.splice',
 | |
|             got  => $offset,
 | |
|             range => (0..^self.elems),
 | |
|         ).fail if $o < 0;
 | |
|         $s //= self.elems - ($o min $elems);
 | |
|         $s = $s(self.elems - $o) if nqp::istype($s, Callable);
 | |
|         X::OutOfRange.new(
 | |
|             what => 'size argument to List.splice',
 | |
|             got  => $size,
 | |
|             range => (0..^(self.elems - $o)),
 | |
|         ).fail if $s < 0;
 | |
| 
 | |
|         my @ret = self[$o..($o + $s - 1)];
 | |
|         nqp::splice($!items,
 | |
|                     nqp::getattr(@values.eager, List, '$!items'),
 | |
|                     $o.Int, $s.Int);
 | |
|         @ret;
 | |
|     }
 | |
| 
 | |
|     method sort($by = &infix:<cmp>) {
 | |
|         fail 'Cannot .sort an infinite list' if self.infinite; #MMD?
 | |
| 
 | |
|         # Instead of sorting elements directly, we sort a Parcel of
 | |
|         # indices from 0..^$list.elems, then use that Parcel as
 | |
|         # a slice into self. This is for historical reasons: on
 | |
|         # Parrot we delegate to RPA.sort. The JVM implementation
 | |
|         # uses a Java collection sort. MoarVM has its sort algorithm
 | |
|         # implemented in NQP.
 | |
| 
 | |
|         # nothing to do here
 | |
|         my $elems := self.elems;
 | |
|         return self if $elems < 2;
 | |
| 
 | |
|         # Range is currently optimized for fast Parcel construction.
 | |
|         my $index := Range.new(0, $elems, :excludes-max).reify(*);
 | |
|         my Mu $index_rpa := nqp::getattr($index, Parcel, '$!storage');
 | |
| 
 | |
|         # if $by.arity < 2, then we apply the block to the elements
 | |
|         # for sorting.
 | |
|         if ($by.?count // 2) < 2 {
 | |
|             my $list = self.map($by).eager;
 | |
|             nqp::p6sort($index_rpa, -> $a, $b { $list.at_pos($a) cmp $list.at_pos($b) || $a <=> $b });
 | |
|         }
 | |
|         else {
 | |
|             my $list = self.eager;
 | |
|             nqp::p6sort($index_rpa, -> $a, $b { $by($list.at_pos($a), $list.at_pos($b)) || $a <=> $b });
 | |
|         }
 | |
|         self[$index];
 | |
|     }
 | |
| 
 | |
|     multi method ACCEPTS(List:D: $topic) { self }
 | |
| 
 | |
|     method uniq(|c) {
 | |
|         DEPRECATED('unique', |<2014.11 2015.11>);
 | |
|         self.unique(|c);
 | |
|     }
 | |
| 
 | |
|     proto method unique(|) {*}
 | |
|     multi method unique() {
 | |
|         my $seen := nqp::hash();
 | |
|         my str $target;
 | |
|         gather for @.list {
 | |
|             $target = nqp::unbox_s($_.WHICH);
 | |
|             unless nqp::existskey($seen, $target) {
 | |
|                 nqp::bindkey($seen, $target, 1);
 | |
|                 take $_;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     multi method unique( :&as!, :&with! ) {
 | |
|         my @seen = "should be Mu, but doesn't work in settings :-("
 | |
|         my Mu $target;
 | |
|         gather for @.list {
 | |
|             $target = &as($_);
 | |
|             if first( { with($target,$_) }, @seen ) =:= Nil {
 | |
|                 @seen.push($target);
 | |
|                 take $_;
 | |
|             }
 | |
|         };
 | |
|     }
 | |
|     multi method unique( :&as! ) {
 | |
|         my $seen := nqp::hash();
 | |
|         my str $target;
 | |
|         gather for @.list {
 | |
|             $target = &as($_).WHICH;
 | |
|             unless nqp::existskey($seen, $target) {
 | |
|                 nqp::bindkey($seen, $target, 1);
 | |
|                 take $_;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     multi method unique( :&with! ) {
 | |
|         nextwith() if &with === &[===]; # use optimized version
 | |
| 
 | |
|         my @seen;  # should be Mu, but doesn't work in settings :-(
 | |
|         my Mu $target;
 | |
|         gather for @.list {
 | |
|             $target := $_;
 | |
|             if first( { with($target,$_) }, @seen ) =:= Nil {
 | |
|                 @seen.push($target);
 | |
|                 take $_;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     my @secret;
 | |
|     proto method squish(|) {*}
 | |
|     multi method squish( :&as!, :&with = &[===] ) {
 | |
|         my $last = @secret;
 | |
|         my str $which;
 | |
|         gather for @.list {
 | |
|             $which = &as($_).Str;
 | |
|             unless with($which,$last) {
 | |
|                 $last = $which;
 | |
|                 take $_;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     multi method squish( :&with = &[===] ) {
 | |
|         my $last = @secret;
 | |
|         gather for @.list {
 | |
|             unless with($_,$last) {
 | |
|                 $last = $_;
 | |
|                 take $_;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     proto method rotor(|) {*}
 | |
|     multi method rotor(1, 0) { self }
 | |
|     multi method rotor($elems = 2, $overlap = 1) {
 | |
|         X::OutOfRange.new(
 | |
|             what => 'Overlap argument to List.rotor',
 | |
|             got  => $overlap,
 | |
|             range => (0 .. $elems - 1),
 | |
|         ).fail unless 0 <= $overlap < $elems;
 | |
|         X::OutOfRange.new(
 | |
|             what => 'Elements argument to List.rotor',
 | |
|             got  => $elems,
 | |
|             range => (0 .. *),
 | |
|         ).fail unless 0 <= $elems;
 | |
| 
 | |
|         my $finished = 0;
 | |
|         gather while $finished + $overlap < self.gimme($finished + $elems) {
 | |
|             take item self[$finished ..^ $finished + $elems];
 | |
|             $finished += $elems - $overlap
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     multi method gist(List:D:) {
 | |
|         @(self).map( -> $elem {
 | |
|             given ++$ {
 | |
|                 when 101 { '...' }
 | |
|                 when 102 { last }
 | |
|                 default  { $elem.gist }
 | |
|             }
 | |
|         } ).join: ' ';
 | |
|     }
 | |
|     multi method perl(List:D \SELF:) {
 | |
|         self.gimme(*);
 | |
|         self.Parcel.perl ~ '.list'
 | |
|           ~ (nqp::iscont(SELF) ?? '.item' !! '')
 | |
|     }
 | |
| 
 | |
|     method REIFY(Parcel \parcel, Mu \nextiter) {
 | |
|         nqp::splice($!items, nqp::getattr(parcel, Parcel, '$!storage'),
 | |
|                     nqp::elems($!items), 0);
 | |
|         nqp::bindattr(self, List, '$!nextiter', nextiter);
 | |
|         parcel
 | |
|     }
 | |
| 
 | |
|     method FLATTENABLE_LIST() { self.gimme(*); $!items }
 | |
|     method FLATTENABLE_HASH() { nqp::hash() }
 | |
| 
 | |
|     multi method DUMP(List:D: :$indent-step = 4, :%ctx?) {
 | |
|         return DUMP(self, :$indent-step) unless %ctx;
 | |
| 
 | |
|         my $flags    := ("\x221e" if self.infinite);
 | |
|         my Mu $attrs := nqp::list();
 | |
|         nqp::push($attrs, '$!flattens');
 | |
|         nqp::push($attrs,  $!flattens );
 | |
|         nqp::push($attrs, '$!items'   );
 | |
|         nqp::push($attrs,  $!items    );
 | |
|         nqp::push($attrs, '$!nextiter');
 | |
|         nqp::push($attrs,  $!nextiter );
 | |
|         self.DUMP-OBJECT-ATTRS($attrs, :$indent-step, :%ctx, :$flags);
 | |
|     }
 | |
| 
 | |
|     multi method keys(List:D:) {
 | |
|         self.values.map: { (state $)++ }
 | |
|     }
 | |
|     multi method kv(List:D:) {
 | |
|         gather for self.values {
 | |
|             take (state $)++;
 | |
|             take-rw $_;
 | |
|         }
 | |
|     }
 | |
|     multi method values(List:D:) {
 | |
|         my Mu $rpa := nqp::clone(nqp::p6listitems(self));
 | |
|         nqp::push($rpa, $!nextiter) if $!nextiter.defined;
 | |
|         nqp::p6list($rpa, List, self.flattens);
 | |
|     }
 | |
|     multi method pairs(List:D:) {
 | |
|         self.values.map: {; (state $)++ => $_ }
 | |
|     }
 | |
| 
 | |
|     method reduce(List: &with) {
 | |
|         fail('can only reduce with arity 2')
 | |
|             unless &with.arity <= 2 <= &with.count;
 | |
|         return unless self.DEFINITE;
 | |
|         my \vals = self.values;
 | |
|         my Mu $val = vals.shift;
 | |
|         $val = with($val, $_) for vals;
 | |
|         $val;
 | |
|     }
 | |
| 
 | |
|     method sink() {
 | |
|         self.gimme(*, :sink) if self.DEFINITE && $!nextiter.DEFINITE;
 | |
|         Nil;
 | |
|     }
 | |
| 
 | |
|     # this is a remnant of a previous implementation of .push(), which
 | |
|     # apparently is used by LoL.  Please remove when no longer necessary.
 | |
|     method STORE_AT_POS(Int \pos, Mu \v) is rw {
 | |
|         nqp::bindpos($!items, nqp::unbox_i(pos), v)
 | |
|     }
 | |
| 
 | |
|     proto method combinations($?) {*}
 | |
|     multi method combinations( Int $of ) {
 | |
|         ([self[@$_]] for combinations(self.elems, $of).eager)
 | |
|     }
 | |
|     multi method combinations( Range $of = 0 .. * ) {
 | |
|         gather for @$of {
 | |
|             last if $_ > self.elems;
 | |
|             take self.combinations($_);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     method permutations() {
 | |
|         # need block on Moar because of RT#121830
 | |
|         gather { take [self[@$_]] for permutations(self.elems).eager }
 | |
|     }
 | |
| }
 | |
| 
 | |
| sub eager(|) {
 | |
|     nqp::p6parcel(nqp::p6argvmarray(), Any).eager
 | |
| }
 | |
| 
 | |
| sub flat(|) {
 | |
|     nqp::p6list(nqp::p6argvmarray(), List, Bool::True)
 | |
| }
 | |
| 
 | |
| sub list(|) {
 | |
|     nqp::p6list(nqp::p6argvmarray(), List, Mu)
 | |
| }
 | |
| 
 | |
| proto sub infix:<xx>(|)       { * }
 | |
| multi sub infix:<xx>()        { fail "No zero-arg meaning for infix:<xx>" }
 | |
| multi sub infix:<xx>(Mu \x)   {x }
 | |
| multi sub infix:<xx>(Mu \x, $n is copy, :$thunked!) {
 | |
|     $n = nqp::p6bool(nqp::istype($n, Whatever)) ?? Inf !! $n.Int;
 | |
|     GatherIter.new({ take x.() while --$n >= 0; }, :infinite($n == Inf)).list
 | |
| }
 | |
| multi sub infix:<xx>(Mu \x, Whatever, :$thunked!) {
 | |
|     GatherIter.new({ loop { take x.() } }, :infinite(True)).flat
 | |
| }
 | |
| multi sub infix:<xx>(Mu \x, Whatever) {
 | |
|     GatherIter.new({ loop { take x } }, :infinite(True)).flat
 | |
| }
 | |
| multi sub infix:<xx>(Mu \x, $n) {
 | |
|     my int $size = $n.Int;
 | |
| 
 | |
|     my Mu $rpa := nqp::list();
 | |
|     if $size > 0 {
 | |
|         nqp::setelems($rpa, $size);
 | |
|         nqp::setelems($rpa, 0);
 | |
| 
 | |
|         $size = $size + 1;
 | |
|         nqp::push($rpa,x) while $size = $size - 1;
 | |
|     }
 | |
| 
 | |
|     nqp::p6parcel($rpa, Any);
 | |
| }
 | |
| 
 | |
| proto sub pop(@) {*}
 | |
| multi sub pop(@a) { @a.pop }
 | |
| 
 | |
| proto sub shift(@) {*}
 | |
| multi sub shift(@a) { @a.shift }
 | |
| 
 | |
| proto sub unshift(|) {*}
 | |
| multi sub unshift(\a, \elem) { a.unshift: elem }
 | |
| multi sub unshift(\a, *@elems) { a.unshift: @elems }
 | |
| 
 | |
| proto sub push(|) {*}
 | |
| multi sub push(\a, \elem) { a.push: elem }
 | |
| multi sub push(\a, *@elems) { a.push: @elems }
 | |
| 
 | |
| sub reverse(*@a)            { @a.reverse }
 | |
| sub rotate(@a, Int $n = 1)  { @a.rotate($n) }
 | |
| sub reduce (&with, *@list)  { @list.reduce(&with) }
 | |
| sub splice(@arr, $offset = 0, $size?, *@values) {
 | |
|     @arr.splice($offset, $size, @values)
 | |
| }
 | |
| 
 | |
| multi sub infix:<cmp>(@a, @b) { (@a Zcmp @b).first(&prefix:<?>) || @a <=> @b }
 | |
| 
 | |
| # vim: ft=perl6 expandtab sw=4
 |