my class X::Seq::Consumed { ... } my class X::Seq::NotIndexable { ... } my class Seq is Cool does Iterable does Sequence { # The underlying iterator that iterating this sequence will work its # way through. Can only be obtained once. has Iterator $!iter; # The only valid way to create a Seq directly is by giving it the # iterator it will consume and maybe memoize. proto method new(Seq: |) {*} multi method new(Seq: Iterator:D $iter) { nqp::p6bindattrinvres(nqp::create(self),Seq,'$!iter',nqp::decont($iter)) } # This candidate exists purely for being able to EVAL a .raku # representation of a Seq of which the iterator has already been taken, multi method new(Seq:) { nqp::create(self) } method iterator(Seq:D:) { nqp::if( nqp::isconcrete(my \iter = $!iter), nqp::stmts( ($!iter := Iterator), iter ), nqp::if( nqp::isconcrete($!list), $!list.iterator, X::Seq::Consumed.new.throw ) ) } multi method is-lazy(Seq:D:) { nqp::isconcrete($!iter) ?? $!iter.is-lazy !! nqp::isconcrete($!list) ?? $!list.is-lazy !! X::Seq::Consumed.new.throw } multi method Seq(Seq:D:) { self } method Capture() { self.List.Capture } method elems() { self.is-lazy ?? self.fail-iterator-cannot-be-lazy('.elems',"") !! nqp::isconcrete($!iter) && nqp::istype($!iter,PredictiveIterator) ?? $!iter.count-only !! self.cache.elems } method Numeric() { self.elems } method Int() { self.elems } method Bool(Seq:D:) { nqp::isconcrete($!iter) && nqp::istype($!iter,PredictiveIterator) ?? $!iter.bool-only !! self.cache.Bool } multi method raku(Seq:D \SELF:) { # If we don't have an iterator, someone grabbed it already; # Check for cached $!list; if that's missing too, we're consumed my $raku; if not $!iter.DEFINITE and not $!list.DEFINITE { # cannot call .cache on a Seq that's already been iterated, # so we need to produce a string that, when EVAL'd, reproduces # an already iterated Seq. # compare https://github.com/Raku/old-issue-tracker/issues/5124 $raku = self.^name ~ '.new()'; } else { $raku = self.cache.raku ~ '.Seq' } nqp::iscont(SELF) ?? '$(' ~ $raku ~ ')' !! $raku } method join(Seq:D: $separator = '' --> Str:D) { nqp::if( (my $iterator := self.iterator).is-lazy, '...', nqp::stmts( (my $strings := nqp::list_s), nqp::until( nqp::eqaddr((my $pulled := $iterator.pull-one),IterationEnd), nqp::push_s($strings,nqp::unbox_s( nqp::if( nqp::isconcrete($pulled) && nqp::istype($pulled,Str), $pulled, nqp::if( nqp::can($pulled,'Str'), $pulled.Str, nqp::box_s($pulled,Str) ) ) )) ), nqp::box_s(nqp::join(nqp::unbox_s($separator.Str),$strings),Str) ) ) } method reverse(--> Seq:D) is nodal { nqp::if( (my $iterator := self.iterator).is-lazy, self.fail-iterator-cannot-be-lazy('.reverse'), nqp::stmts( $iterator.push-all(my \buffer := nqp::create(IterationBuffer)), Seq.new: Rakudo::Iterator.ReifiedReverse(buffer, Mu) ) ) } method rotate(Int(Cool) $rotate = 1 --> Seq:D) is nodal { nqp::if( (my $iterator := self.iterator).is-lazy, self.fail-iterator-cannot-be-lazy('.rotate'), nqp::if( $rotate, Seq.new( nqp::if( $rotate > 0, Rakudo::Iterator.Rotate($rotate, $iterator), nqp::stmts( $iterator.push-all(my \buffer := nqp::create(IterationBuffer)), Rakudo::Iterator.ReifiedRotate($rotate, buffer, Mu) ) )), self ) ) } multi method slice(Seq:D: Iterable:D \iterable --> Seq:D) { Seq.new( Rakudo::Iterator.MonotonicIndexes( self.iterator, iterable.iterator, 0, -> $index, $next { die "Provided index $index, which is lower than $next"; } ) ) } multi method slice(Seq:D: *@indices --> Seq:D) { self.slice(@indices) } method sink(--> Nil) { nqp::if( nqp::isconcrete($!iter), nqp::stmts( $!iter.sink-all, ($!iter := Iterator) ), nqp::if( nqp::isconcrete($!list), $!list.sink ) ) } # This method is mainly called from Actions.nqp proto method from-loop(|) {*} multi method from-loop(&body, :$label) { Seq.new: Rakudo::Iterator.Loop(&body, $label) } multi method from-loop(&body, &cond, :$repeat!, :$label) { Seq.new: $repeat ?? Rakudo::Iterator.RepeatLoop(&body, &cond, $label) !! Rakudo::Iterator.WhileLoop(&body, &cond, $label) } multi method from-loop(&body, &cond, :$label) { Seq.new: Rakudo::Iterator.WhileLoop(&body, &cond, $label) } multi method from-loop(&body, &cond, &afterwards, :$label) { Seq.new: Rakudo::Iterator.CStyleLoop(&body, &cond, &afterwards, $label) } multi method ACCEPTS(Seq:D: Iterable:D \iterable --> Bool:D) { nqp::if( (my \riter := self.iterator).is-lazy, False, nqp::if( (my \liter := iterable.iterator).is-lazy, False, nqp::stmts( nqp::until( nqp::eqaddr((my \left := liter.pull-one),IterationEnd), nqp::if( nqp::eqaddr((my \right := riter.pull-one),IterationEnd) || nqp::not_i(right.ACCEPTS(left)), (return False) ) ), nqp::hllbool(nqp::eqaddr(riter.pull-one,IterationEnd)) ) ) ) } } sub GATHER(&block) is implementation-detail { Seq.new(Rakudo::Iterator.Gather(&block)) } # vim: expandtab shiftwidth=4