#!/usr/bin/env raku # This script reads the native_array.rakumod file, and generates the intarray, # numarray and strarray roles in it, and writes it back to the file. # always use highest version of Raku use v6.*; my $generator = $*PROGRAM-NAME; my $generated = DateTime.now.gist.subst(/\.\d+/,''); my $start = '#- start of generated part of '; my $idpos = $start.chars; my $idchars = 3; my $end = '#- end of generated part of '; # slurp the whole file and set up writing to it my $filename = "src/core.c/native_array.rakumod"; my @lines = $filename.IO.lines; $*OUT = $filename.IO.open(:w); my %type_mapper = ( int => ( :coerce, :iseq_postfix, :postfix, :list_postfix, :nullval("0"), :push_postfix, :type, :Type, :value, :Value, ).Map, num => ( :coerce('nqp::coerce_ns'), :iseq_postfix, :postfix, :list_postfix, :nullval("0e0"), :push_postfix, :type, :Type, :value, :Value, ).Map, str => ( :coerce(''), :iseq_postfix, :postfix, :list_postfix, :nullval('""'), :push_postfix, :type, :Type, :value, :Value, ).Map, uint => ( :coerce, :iseq_postfix, :postfix, :list_postfix, :nullval("0"), :push_postfix, :type, :Type, :value, :Value, ).Map, ); # for all the lines in the source that don't need special handling while @lines { my $line := @lines.shift; # nothing to do yet unless $line.starts-with($start) { say $line; next; } # found shaped header, ignore my $type = $line.substr($idpos,$idchars); if $type eq 'sha' { say $line; next; } # found header, check validity and set up mapper $type = "uint" if $type eq "uin"; die "Don't know how to handle $type" unless my %mapper := %type_mapper{$type}; say $start ~ $type ~ "array role -----------------------------------"; say "#- Generated on $generated by $generator"; say "#- PLEASE DON'T CHANGE ANYTHING BELOW THIS LINE"; # skip the old version of the code while @lines { last if @lines.shift.starts-with($end); } # spurt the role say Q:to/SOURCE/.subst(/ '#' (\w+) '#' /, -> $/ { %mapper{$0} }, :g).chomp; multi method grep(#type#array:D: #Value#:D $needle, :$k, :$kv, :$p, :$v --> Seq:D) { my int $i = -1; my uint $elems = nqp::elems(self); my $result := nqp::create(IterationBuffer); if $k { nqp::while( nqp::islt_i(++$i,$elems), nqp::if( nqp::iseq_#iseq_postfix#(nqp::atpos_#postfix#(self,$i),$needle), nqp::push($result,nqp::clone($i)) ) ); } elsif $kv { nqp::while( nqp::islt_i(++$i,$elems), nqp::if( nqp::iseq_#iseq_postfix#(nqp::atpos_#postfix#(self,$i),$needle), nqp::stmts( nqp::push($result,nqp::clone($i)), nqp::push($result,$needle) ) ) ); } elsif $p { nqp::while( nqp::islt_i(++$i,$elems), nqp::if( nqp::iseq_#iseq_postfix#(nqp::atpos_#postfix#(self,$i),$needle), nqp::push($result,Pair.new($i,$needle)) ) ); } else { nqp::while( nqp::islt_i(++$i,$elems), nqp::if( nqp::iseq_#iseq_postfix#(nqp::atpos_#postfix#(self,$i),$needle), nqp::push($result,$needle) ) ); } $result.Seq } multi method head(#type#array:D:) { nqp::atpos_#postfix#(self,0) } multi method tail(#type#array:D:) { nqp::atpos_#postfix#(self,nqp::sub_i(nqp::elems(self),1)) } multi method first(#type#array:D: #Value#:D $needle, :$k, :$kv, :$p, :$v) { my int $i = -1; my uint $elems = nqp::elems(self); nqp::while( nqp::islt_i(++$i,$elems) && nqp::isne_#iseq_postfix#(nqp::atpos_#postfix#(self,$i),$needle), nqp::null() ); nqp::iseq_i($i,$elems) ?? Nil !! $k ?? $i !! $kv ?? ($i,$needle) !! $p ?? Pair.new($i,$needle) !! $needle } multi method unique(#type#array:D:) { my int $i = -1; my uint $elems = nqp::elems(self); my $result := nqp::create(array[self.of]); my $seen := nqp::hash; nqp::while( nqp::islt_i(++$i,$elems), nqp::unless( nqp::existskey( $seen, (my str $key = #coerce#( my #type# $value = nqp::atpos_#postfix#(self,$i) )) ), nqp::stmts( nqp::push_#push_postfix#($result,$value), nqp::bindkey($seen,$key,1), ) ) ); $result } multi method repeated(#type#array:D:) { my int $i = -1; my uint $elems = nqp::elems(self); my $result := nqp::create(array[self.of]); my $seen := nqp::hash; nqp::while( nqp::islt_i(++$i,$elems), nqp::if( nqp::existskey( $seen, (my str $key = #coerce#( my #type# $value = nqp::atpos_#postfix#(self,$i) )) ), nqp::push_#push_postfix#($result,$value), nqp::bindkey($seen,$key,1) ) ); $result } multi method squish(#type#array:D:) { if nqp::elems(self) -> int $elems { my $result := nqp::create(array[self.of]); my #type# $last = nqp::push_#push_postfix#($result,nqp::atpos_#postfix#(self,0)); my uint $i; nqp::while( nqp::islt_i(++$i,$elems), nqp::if( nqp::isne_#iseq_postfix#(nqp::atpos_#postfix#(self,$i),$last), nqp::push_#push_postfix#($result,$last = nqp::atpos_#postfix#(self,$i)) ) ); $result } else { self } } multi method AT-POS(#type#array:D: uint $idx --> #type#) is raw { nqp::atposref_#postfix#(self,$idx) } multi method AT-POS(#type#array:D: Int:D $idx --> #type#) is raw { $idx < 0 ?? INDEX_OUT_OF_RANGE($idx) !! nqp::atposref_#postfix#(self,$idx) } multi method ASSIGN-POS(#type#array:D: uint $idx, #type# $value --> #type#) { nqp::bindpos_#postfix#(self, $idx, $value) } multi method ASSIGN-POS(#type#array:D: Int:D $idx, #type# $value --> #type#) { $idx < 0 ?? INDEX_OUT_OF_RANGE($idx) !! nqp::bindpos_#postfix#(self, $idx, $value) } multi method ASSIGN-POS(#type#array:D: uint $idx, #Value#:D $value --> #type#) { nqp::bindpos_#postfix#(self, $idx, $value) } multi method ASSIGN-POS(#type#array:D: Int:D $idx, #Value#:D $value --> #type#) { $idx < 0 ?? INDEX_OUT_OF_RANGE($idx) !! nqp::bindpos_#postfix#(self, $idx, $value) } multi method ASSIGN-POS(#type#array:D: Any $idx, Mu \value --> Nil) { X::TypeCheck.new( operation => "assignment to #type# array element #$idx", got => value, expected => T, ).throw; } multi method STORE(#type#array:D: $value --> #type#array:D) { nqp::setelems(self,1); nqp::bindpos_#postfix#(self, 0, nqp::unbox_#postfix#($value)); self } multi method STORE(#type#array:D: #type#array:D \values --> #type#array:D) { nqp::setelems(self,nqp::elems(values)); nqp::splice(self,values,0,nqp::elems(values)) } multi method STORE(#type#array:D: Seq:D $seq --> #type#array:D) { nqp::if( (my $iterator := $seq.iterator).is-lazy, self.throw-iterator-cannot-be-lazy('store'), nqp::stmts( nqp::setelems(self,0), $iterator.push-all(self), self ) ) } multi method STORE(#type#array:D: List:D \values --> #type#array:D) { my uint $elems = values.elems; # reifies my $reified := nqp::getattr(values,List,'$!reified'); nqp::setelems(self, $elems); my int $i = -1; nqp::while( nqp::islt_i(++$i,$elems), nqp::bindpos_#postfix#(self,$i, nqp::if( nqp::isnull(nqp::atpos($reified,$i)), #nullval#, nqp::unbox_#postfix#(nqp::atpos($reified,$i)) ) ) ); self } multi method STORE(#type#array:D: @values --> #type#array:D) { my uint $elems = @values.elems; # reifies nqp::setelems(self, $elems); my int $i = -1; nqp::while( nqp::islt_i(++$i,$elems), nqp::bindpos_#postfix#(self, $i, nqp::unbox_#postfix#(@values.AT-POS($i))) ); self } multi method push(#type#array:D: #type# $value --> #type#array:D) { nqp::push_#push_postfix#(self, $value); self } multi method push(#type#array:D: #Value#:D $value --> #type#array:D) { nqp::push_#push_postfix#(self, $value); self } multi method push(#type#array:D: Mu \value --> Nil) { X::TypeCheck.new( operation => 'push to #type# array', got => value, expected => T, ).throw; } multi method append(#type#array:D: #type# $value --> #type#array:D) { nqp::push_#push_postfix#(self, $value); self } multi method append(#type#array:D: #Value#:D $value --> #type#array:D) { nqp::push_#push_postfix#(self, $value); self } multi method append(#type#array:D: #type#array:D $values --> #type#array:D) is default { nqp::splice(self,$values,nqp::elems(self),0) } multi method append(#type#array:D: @values --> #type#array:D) { return self.fail-iterator-cannot-be-lazy('.append') if @values.is-lazy; nqp::push_#push_postfix#(self, $_) for flat @values; self } method pop(#type#array:D: --> #type#) { nqp::elems(self) ?? nqp::pop_#push_postfix#(self) !! self.throw-cannot-be-empty('pop') } method shift(#type#array:D: --> #type#) { nqp::elems(self) ?? nqp::shift_#push_postfix#(self) !! self.throw-cannot-be-empty('shift') } multi method unshift(#type#array:D: #type# $value --> #type#array:D) { nqp::unshift_#push_postfix#(self, $value); self } multi method unshift(#type#array:D: #Value#:D $value --> #type#array:D) { nqp::unshift_#push_postfix#(self, $value); self } multi method unshift(#type#array:D: @values --> #type#array:D) { return self.fail-iterator-cannot-be-lazy('.unshift') if @values.is-lazy; nqp::unshift_#push_postfix#(self, @values.pop) while @values; self } multi method unshift(#type#array:D: Mu \value --> Nil) { X::TypeCheck.new( operation => 'unshift to #type# array', got => value, expected => T, ).throw; } my $empty_#postfix# := nqp::list_#list_postfix#; multi method splice(#type#array:D: --> #type#array:D) { my $splice := nqp::clone(self); nqp::setelems(self,0); $splice } multi method splice(#type#array:D: Int:D $got --> #type#array:D) { nqp::if( nqp::islt_i($got,0) || nqp::isgt_i( (my uint $offset = $got), (my uint $elems = nqp::elems(self)) ), X::OutOfRange.new( :what('Offset argument to splice'), :$got, :range("0..$elems") ).Failure, nqp::if( nqp::iseq_i($offset,$elems), nqp::create(self.WHAT), nqp::stmts( (my $slice := nqp::slice(self,$offset,-1)), nqp::splice( self, $empty_#postfix#, $offset, nqp::sub_i($elems,$offset) ), $slice ) ) ) } multi method splice(#type#array:D: Int:D $offset, Int:D $size --> #type#array:D) { nqp::unless( nqp::istype( (my $slice := CLONE_SLICE(self,$offset,$size)), Failure ), nqp::splice(self,$empty_#postfix#,$offset,$size) ); $slice } multi method splice(#type#array:D: Int:D $offset, Int:D $size, #type#array:D \values --> #type#array:D) { nqp::unless( nqp::istype( (my $slice := CLONE_SLICE(self,$offset,$size)), Failure ), nqp::splice( self, nqp::if(nqp::eqaddr(self,values),nqp::clone(values),values), $offset, $size ) ); $slice } multi method splice(#type#array:D: Int:D $offset, Int:D $size, Seq:D $seq --> #type#array:D) { nqp::if( $seq.is-lazy, self.throw-iterator-cannot-be-lazy('.splice'), nqp::stmts( nqp::unless( nqp::istype( (my $slice := CLONE_SLICE(self,$offset,$size)), Failure ), nqp::splice(self,nqp::create(self).STORE($seq),$offset,$size) ), $slice ) ) } multi method splice(#type#array:D: $offset=0, $size=Whatever, *@values --> #type#array:D) { return self.fail-iterator-cannot-be-lazy('splice in') if @values.is-lazy; my int $elems = nqp::elems(self); # XXX execution error on next line if uint my int $o = nqp::istype($offset,Callable) ?? $offset($elems) !! nqp::istype($offset,Whatever) ?? $elems !! $offset.Int; my int $s = nqp::istype($size,Callable) ?? $size($elems - $o) !! !defined($size) || nqp::istype($size,Whatever) ?? $elems - ($o min $elems) !! $size.Int; unless nqp::istype( (my $splice := CLONE_SLICE(self,$o,$s)), Failure ) { my $splicees := nqp::create(self); nqp::push_#push_postfix#($splicees, @values.shift) while @values; nqp::splice(self,$splicees,$o,$s); } $splice } multi method min(#type#array:D:) { nqp::if( (my uint $elems = nqp::elems(self)), nqp::stmts( (my uint $i), (my #type# $min = nqp::atpos_#postfix#(self,0)), nqp::while( nqp::islt_i(++$i,$elems), nqp::if( nqp::islt_#iseq_postfix#(nqp::atpos_#postfix#(self,$i),$min), ($min = nqp::atpos_#postfix#(self,$i)) ) ), $min ), Inf ) } multi method max(#type#array:D:) { nqp::if( (my uint $elems = nqp::elems(self)), nqp::stmts( (my uint $i), (my #type# $max = nqp::atpos_#postfix#(self,0)), nqp::while( nqp::islt_i(++$i,$elems), nqp::if( nqp::isgt_#iseq_postfix#(nqp::atpos_#postfix#(self,$i),$max), ($max = nqp::atpos_#postfix#(self,$i)) ) ), $max ), -Inf ) } multi method minmax(#type#array:D: --> Range:D) { nqp::if( (my uint $elems = nqp::elems(self)), nqp::stmts( (my uint $i), (my #type# $min = my #type# $max = nqp::atpos_#postfix#(self,0)), nqp::while( nqp::islt_i(++$i,$elems), nqp::if( nqp::islt_#iseq_postfix#(nqp::atpos_#postfix#(self,$i),$min), ($min = nqp::atpos_#postfix#(self,$i)), nqp::if( nqp::isgt_#iseq_postfix#(nqp::atpos_#postfix#(self,$i),$max), ($max = nqp::atpos_#postfix#(self,$i)) ) ) ), Range.new($min,$max) ), Range.new(Inf,-Inf) ) } method iterator(#type#array:D: --> PredictiveIterator:D) { Rakudo::Iterator.native_#postfix#(self) } method Seq(#type#array:D: --> Seq:D) { Seq.new(Rakudo::Iterator.native_#postfix#(self)) } method reverse(#type#array:D: --> #type#array:D) is nodal { nqp::stmts( (my uint $elems = nqp::elems(self)), (my int $last = nqp::sub_i($elems,1)), (my int $i = -1), (my $to := nqp::clone(self)), nqp::while( nqp::islt_i(++$i,$elems), nqp::bindpos_#postfix#($to,nqp::sub_i($last,$i), nqp::atpos_#postfix#(self,$i)) ), $to ) } method rotate(#type#array:D: Int(Cool) $rotate = 1 --> #type#array:D) is nodal { my int $elems = nqp::elems(self); my $to := nqp::clone(self); my int $i = -1; my int $j = nqp::mod_i( nqp::sub_i(nqp::sub_i($elems,1),$rotate), $elems ); $j = nqp::add_i($j,$elems) if nqp::islt_i($j,0); nqp::while( nqp::islt_i(++$i,$elems), nqp::bindpos_#postfix#( $to, ($j = nqp::mod_i(nqp::add_i($j,1),$elems)), nqp::atpos_#postfix#(self,$i) ), ); $to } multi method sort(#type#array:D: --> #type#array:D) { Rakudo::Sorting.MERGESORT-#type#(nqp::clone(self)) } multi method ACCEPTS(#type#array:D: #type#array:D \o --> Bool:D) { nqp::hllbool( nqp::unless( nqp::eqaddr(self,my $other := nqp::decont(o)), nqp::if( nqp::iseq_i( (my uint $elems = nqp::elems(self)), nqp::elems($other) ), nqp::stmts( (my int $i = -1), nqp::while( nqp::islt_i(++$i,$elems) && nqp::iseq_#iseq_postfix#( nqp::atpos_#postfix#(self,$i), nqp::atpos_#postfix#($other,$i) ), nqp::null ), nqp::iseq_i($i,$elems) ) ) ) ) } proto method grab(|) {*} multi method grab(#type#array:D: --> #type#) { nqp::elems(self) ?? self.GRAB_ONE !! Nil } multi method grab(#type#array:D: Callable:D $calculate --> #type#) { self.grab($calculate(nqp::elems(self))) } multi method grab(#type#array:D: Whatever --> Seq:D) { self.grab(Inf) } my class GrabN does Iterator { has $!array; has uint $!count; method !SET-SELF(\array,\count) { nqp::stmts( (my uint $elems = nqp::elems(array)), ($!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($!array), nqp::stmts( --$!count, $!array.GRAB_ONE ), IterationEnd ) } method is-deterministic(--> False) { } } multi method grab(#type#array:D: \count --> Seq:D) { Seq.new( nqp::elems(self) ?? GrabN.new(self,count) !! Rakudo::Iterator.Empty ) } method GRAB_ONE(#type#array:D: --> #type#) is implementation-detail { nqp::stmts( (my $value := nqp::atpos_#postfix#( self, (my uint $pos = nqp::floor_n(nqp::rand_n(nqp::elems(self)))) )), nqp::splice(self,$empty_#postfix#,$pos,1), $value ) } SOURCE # we're done for this role say "#- PLEASE DON'T CHANGE ANYTHING ABOVE THIS LINE"; say $end ~ $type ~ "array role -------------------------------------"; } # close the file properly $*OUT.close; # vim: expandtab sw=4