# This file automatically generated by /friends/.rakubrew/versions/moar-main/tools/build/gen-cat.nqp #line 1 SETTING::src/core.e/core_prologue.rakumod use MONKEY; # This constant specifies the current CORE revision. It must precede class # declarations to allow correct recording of their respective language version. my constant CORE-SETTING-REV = nqp::box_i(3, Metamodel::Configuration.language_revision_type); #line 1 SETTING::src/core.e/PseudoStash.rakumod my class PseudoStash is CORE::v6c::PseudoStash { has $!package; # Parent package, for which we serve as .WHO has $!walker; # Lookup modes. Must be kept in sync with CORE::v6c::PseudoStash mode constants. my int constant PICK_CHAIN_BY_NAME = 0; my int constant STATIC_CHAIN = 1; my int constant DYNAMIC_CHAIN = 2; my int constant PRECISE_SCOPE = 4; my int constant REQUIRE_DYNAMIC = 8; # A convenience shortcut my constant PseudoStash6c = CORE::v6c::PseudoStash; method new(Mu :$ctx is raw, :$mode = STATIC_CHAIN) { my $stash := nqp::create(self); my Mu $dctx := nqp::decont($ctx); $dctx := nqp::ctxcaller(nqp::ctx()) unless nqp::defined($dctx); nqp::bindattr($stash, PseudoStash6c, '$!ctx', nqp::decont($dctx)); nqp::bindattr_i($stash, PseudoStash6c, '$!mode', nqp::decont($mode)); # $!storage maps symbol names into symbol information. The information can be one of: # - BOOTContext or BOOTHash (nqp::ctxlexpad, nqp::hash) for symbol tables obtained either from a context or # from a Stash # - a CtxDynThunk instance for a dynamic symbol sourced from GLOBAL or PROCESS namespaces # - an Exception instance for symbols which exists but are not accessible via this PseudoStash due to its mode # All values in $!storage hash are choosen to minimize memory footprint of the structure. All of them either # taken from where they already exists anyway (like lexpads), or are typeobjects (kind of the previous case), # or allocated once ever, like GLOBAL/PROCESS thunking objects. nqp::p6bindattrinvres($stash, Map, '$!storage', nqp::hash()) } my Int $id = 0; method NEW-PACKAGE(:$name = "", *%initargs ) is raw is implementation-detail { my $stash := self.new(|%initargs); nqp::setwho( nqp::bindattr($stash, PseudoStash, '$!package', Metamodel::ModuleHOW.new_type(:$name)), $stash) } my $pseudoers := nqp::hash( 'MY', sub ($cur) { PseudoStash.NEW-PACKAGE( :ctx(nqp::getattr(nqp::decont($cur), PseudoStash6c, '$!ctx')), :mode(PRECISE_SCOPE), :name ) }, 'CORE', sub ($cur) { my Mu $ctx := nqp::getattr(nqp::decont($cur), PseudoStash6c, '$!ctx'); until nqp::isnull($ctx) || nqp::existskey(nqp::ctxlexpad($ctx), 'CORE-SETTING-REV') { $ctx := nqp::ctxouterskipthunks($ctx); } nqp::if( nqp::isnull($ctx), Nil, PseudoStash.NEW-PACKAGE( :$ctx, :mode(STATIC_CHAIN), :name )) }, 'CALLER', sub ($cur) { nqp::if( nqp::isnull( my Mu $ctx := nqp::ctxcallerskipthunks( nqp::getattr(nqp::decont($cur), PseudoStash6c, '$!ctx'))), Nil, PseudoStash.NEW-PACKAGE( :$ctx, :mode(PRECISE_SCOPE), :name )) }, 'OUTER', sub ($cur) is raw { my Mu $ctx := nqp::ctxouterskipthunks( nqp::getattr(nqp::decont($cur), PseudoStash6c, '$!ctx')); nqp::isnull($ctx) ?? Nil !! PseudoStash.NEW-PACKAGE( :$ctx, :mode(PRECISE_SCOPE), :name ) }, 'LEXICAL', sub ($cur) { PseudoStash.NEW-PACKAGE( :ctx(nqp::getattr(nqp::decont($cur), PseudoStash6c, '$!ctx')), :mode(STATIC_CHAIN +| DYNAMIC_CHAIN), :name ) }, 'OUTERS', sub ($cur) { my Mu $ctx := nqp::ctxouterskipthunks( nqp::getattr(nqp::decont($cur), PseudoStash6c, '$!ctx')); nqp::isnull($ctx) ?? Nil !! PseudoStash.NEW-PACKAGE( :$ctx, :mode(STATIC_CHAIN), :name ) }, 'DYNAMIC', sub ($cur) { PseudoStash.NEW-PACKAGE( :ctx(nqp::getattr(nqp::decont($cur), PseudoStash6c, '$!ctx')), :mode(DYNAMIC_CHAIN +| REQUIRE_DYNAMIC), :name ) }, 'CALLERS', sub ($cur) { my Mu $ctx := nqp::ctxcallerskipthunks( nqp::getattr(nqp::decont($cur), PseudoStash6c, '$!ctx')); nqp::isnull($ctx) ?? Nil !! PseudoStash.NEW-PACKAGE( :$ctx, :mode(DYNAMIC_CHAIN +| REQUIRE_DYNAMIC), :name ) }, 'UNIT', sub ($cur) { my Mu $ctx := nqp::getattr(nqp::decont($cur), PseudoStash6c, '$!ctx'); until nqp::isnull($ctx) || nqp::existskey(nqp::ctxlexpad($ctx), '!UNIT_MARKER') { $ctx := nqp::ctxouterskipthunks($ctx); } nqp::isnull($ctx) ?? Nil !! PseudoStash.NEW-PACKAGE( :$ctx, :mode(STATIC_CHAIN), :name ) }, 'SETTING', sub ($cur) { # Same as UNIT, but go a little further out (two steps, for # internals reasons). my Mu $ctx := nqp::getattr(nqp::decont($cur), PseudoStash6c, '$!ctx'); until nqp::isnull($ctx) || (nqp::existskey(nqp::ctxlexpad($ctx), '!UNIT_MARKER') && !nqp::existskey(nqp::ctxlexpad($ctx), '!EVAL_MARKER')) { $ctx := nqp::ctxouterskipthunks($ctx); } # EVAL adds two extra contexts to EVAL'ed code. my $outers = ($ctx && nqp::existskey(nqp::ctxlexpad($ctx), '!EVAL_MARKER')) ?? 4 !! 2; $outers-- if nqp::existskey(nqp::ctxlexpad($ctx), '!RAKUAST_MARKER'); nqp::until( (nqp::isnull($ctx) || !$outers), nqp::stmts( ($ctx := nqp::ctxouter($ctx)), ($outers--)) ); nqp::isnull($ctx) ?? Nil !! PseudoStash.NEW-PACKAGE( :$ctx, :mode(STATIC_CHAIN), :name ) }, 'CLIENT', sub ($cur) { my $pkg := nqp::getlexrel( nqp::getattr(nqp::decont($cur), PseudoStash6c, '$!ctx'), '$?PACKAGE'); my Mu $ctx := nqp::ctxcallerskipthunks( nqp::getattr(nqp::decont($cur), PseudoStash6c, '$!ctx')); while nqp::eqaddr(nqp::getlexrel($ctx, '$?PACKAGE'), $pkg) { $ctx := nqp::ctxcallerskipthunks($ctx); die "No client package found" unless $ctx; } PseudoStash.NEW-PACKAGE( :$ctx, :mode(PRECISE_SCOPE), :name ) }, 'OUR', sub ($cur) { nqp::getlexrel( nqp::getattr(nqp::decont($cur), PseudoStash6c, '$!ctx'), '$?PACKAGE') } ); method !find-rev-core($key) { my $p6rev = nqp::substr($key, 2, 1); my $rev = nqp::getcomp('Raku').lvs.internal-from-p6: nqp::substr($key, 2, 1); my $ctx := nqp::getattr(self, PseudoStash6c, '$!ctx'); my $found := nqp::null(); my $stash; nqp::while( $ctx && !$found, nqp::stmts( (my $lexpad := nqp::ctxlexpad($ctx)), nqp::if( nqp::existskey($lexpad, 'CORE-SETTING-REV') && $rev == nqp::atkey($lexpad, 'CORE-SETTING-REV'), ($found := $ctx), ($ctx := nqp::ctxouterskipthunks($ctx))))); nqp::if( nqp::isnull($found), X::NoCoreRevision.new(lang-rev => $p6rev).Failure, PseudoStash.NEW-PACKAGE( :ctx($found), :mode(PRECISE_SCOPE), :name("CORE::$key"))) } # Interface between GLOBAL and PROCESS and dynamic chain pseudos. Implements mapping of twigilled symbol names # into twigilless for atkey/bindkey my class CtxDynThunk { has Mu $!stash; method !SET-SELF(\package) { $!stash := package.WHO; self } method new(\package) { nqp::create(self)!SET-SELF(package) } method atkey(\sym) is raw { # nqp::atkey($!storage, nqp::replace(sym, 1, 1, '')) $!stash.AT-KEY: nqp::replace(sym, 1, 1, '') } method bindkey(\sym, \val) { # nqp::bindkey($!storage, nqp::replace(sym, 1, 1, ''), val) $!stash.BIND-KEY: nqp::replace(sym, 1, 1, ''), val } } # Walks over contexts, respects combined chains (DYNAMIC_CHAIN +| STATIC_CHAIN). In the combined case the inital # context would be tried for each mode. my class CtxWalker { has Mu $!start-ctx; # Stash context – this is where we start from. has Mu $!ctx; # Current context. has Mu $!fallbacks; # List of possible fallbacks to iterate over has Mu $!stash-mode; has Mu $!modes; has $!cur-mode; has $!pick-fallback; has $.exhausted; my CtxDynThunk $GLOBAL-thunk; my CtxDynThunk $PROCESS-thunk; class Return { has Mu $.lexpad is built(:bind); has int $.mode is built(:bind); has Mu $.sym-info is built(:bind); } method !SET-SELF(CtxWalker:D: PseudoStash:D \pseudo) { $!start-ctx := nqp::getattr(pseudo, PseudoStash6c, '$!ctx'); $!ctx := nqp::null(); $!stash-mode := nqp::getattr_i(pseudo, PseudoStash6c, '$!mode'); $!modes := nqp::list_i(); nqp::push_i($!modes, $_) for (PRECISE_SCOPE, STATIC_CHAIN, DYNAMIC_CHAIN).grep({ nqp::bitand_i($_, $!stash-mode) }); $!fallbacks := nqp::list(); $!pick-fallback := 0; $!exhausted := 0; self } method new(PseudoStash:D \pseudo) { nqp::create(self)!SET-SELF(pseudo) } method !next-mode() is raw { nqp::if( nqp::elems($!modes), nqp::stmts( ($!ctx := $!start-ctx), ($!cur-mode := nqp::shift_i($!modes)), nqp::if( nqp::iseq_i($!cur-mode, STATIC_CHAIN), nqp::unless( nqp::isnull(my $OUR-PKG := nqp::getlexrel($!start-ctx, '$?PACKAGE')), nqp::push($!fallbacks, $OUR-PKG.WHO)), nqp::if( nqp::iseq_i($!cur-mode, DYNAMIC_CHAIN), nqp::stmts( nqp::unless( nqp::isnull(my \promise = nqp::getlexreldyn($!start-ctx, '$*PROMISE')), nqp::push($!fallbacks, promise)), nqp::unless( nqp::defined($GLOBAL-thunk), nqp::cas($GLOBAL-thunk, CtxDynThunk, CtxDynThunk.new(nqp::getcurhllsym('GLOBAL')))), nqp::push($!fallbacks, nqp::decont($GLOBAL-thunk)), nqp::unless( nqp::defined($PROCESS-thunk), nqp::cas($PROCESS-thunk, CtxDynThunk, CtxDynThunk.new(nqp::getcurhllsym('PROCESS')))), nqp::push($!fallbacks, nqp::decont($PROCESS-thunk))), nqp::unless( nqp::iseq_i($!cur-mode, PRECISE_SCOPE), nqp::die("Unknown pseudo-stash mode " ~ $!cur-mode))))), nqp::stmts( ($!ctx := nqp::null()), ($!exhausted := 1))); } method next-ctx() is raw { self!next-mode if nqp::isnull($!ctx); return Nil if $!exhausted; my Mu $lexpad; my Mu $sym-info; nqp::stmts( ($lexpad := nqp::if( nqp::istype($!ctx, Map), # For a Stash fallback: OUR:: for static chain ($sym-info := nqp::getattr($!ctx, Map, '$!storage')), nqp::if( nqp::istype($!ctx, CtxDynThunk), # For dynamic chain mapping into GLOBAL/PROCESS nqp::getattr(nqp::getattr(($sym-info := $!ctx), CtxDynThunk, '$!stash'), Map, '$!storage'), # At this point the only possible value is BOOTContext ($sym-info := nqp::ctxlexpad($!ctx))))), nqp::if( nqp::bitand_i($!stash-mode, PRECISE_SCOPE), ($!ctx := nqp::null()), nqp::repeat_while( (nqp::isnull($!ctx) && $!pick-fallback), # Switch to fallback if a context chain is exhausted nqp::if( $!pick-fallback, nqp::if( nqp::elems($!fallbacks), nqp::stmts( ($!ctx := nqp::shift($!fallbacks)), nqp::if( nqp::istype($!ctx, Promise), nqp::stmts( ($!ctx := nqp::getattr($!ctx, Promise, '$!dynamic_context')), ($!pick-fallback := 0)))), nqp::stmts( ($!ctx := nqp::null()), ($!pick-fallback := 0))), nqp::stmts( nqp::if( nqp::iseq_i($!cur-mode, DYNAMIC_CHAIN), ($!ctx := nqp::ctxcallerskipthunks($!ctx))), nqp::if( nqp::iseq_i($!cur-mode, STATIC_CHAIN), ($!ctx := nqp::ctxouterskipthunks($!ctx))), nqp::ifnull( # Ran through a chain till the end $!ctx, ($!pick-fallback := 1)))))), Return.new(:$lexpad, :$sym-info, :mode($!cur-mode))) } } method REIFY-STORAGE(Str :$until-symbol) is implementation-detail { my Mu $storage := nqp::getattr(self, Map, '$!storage'); return 1 if $until-symbol && nqp::existskey($storage, $until-symbol); nqp::unless(nqp::isconcrete($!walker), ($!walker := CtxWalker.new(self))); return 0 if $!walker.exhausted; my Mu $mode := nqp::getattr(self, PseudoStash6c, '$!mode'); my Mu $found := 0; nqp::while( ((my $ctx-info := $!walker.next-ctx) && !$found), nqp::stmts( (my Mu $iter := nqp::iterator(nqp::getattr($ctx-info, CtxWalker::Return, '$!lexpad'))), nqp::while( $iter, nqp::stmts( (my Mu $sym := nqp::iterkey_s(nqp::shift($iter))), nqp::if( nqp::istype($ctx-info.sym-info, CtxDynThunk), ($sym := nqp::replace($sym, 1, 0, '*'))), nqp::if( nqp::defined($until-symbol), nqp::unless($found, ($found := nqp::iseq_s($sym, $until-symbol)))), nqp::unless( nqp::existskey($storage, $sym), # Skip if already encountered nqp::stmts( (my Mu $val := nqp::iterval($iter)), (my Mu $is-dynamic := nqp::iseq_i(nqp::ord($sym, 1), 42)), # has * twigil (my Mu $sym-info := nqp::if( (nqp::bitand_i($mode, REQUIRE_DYNAMIC) || nqp::iseq_i($ctx-info.mode, DYNAMIC_CHAIN)), nqp::if( ($is-dynamic || nqp::if(nqp::iscont($val), $val.VAR.dynamic)), nqp::getattr($ctx-info, CtxWalker::Return, '$!sym-info'), X::Symbol::NotDynamic), nqp::if( (!$is-dynamic || nqp::bitand_i($mode, PRECISE_SCOPE)), nqp::getattr($ctx-info, CtxWalker::Return, '$!sym-info'), X::Symbol::NotLexical))), nqp::bindkey($storage, $sym, $sym-info))))))); $found } my sub SYM-VALUE(Mu \sym-info, \sym-key) is raw { nqp::if( nqp::istype(sym-info, CtxDynThunk), (sym-info.atkey(sym-key)), nqp::if( nqp::istype(sym-info, Exception), sym-info, nqp::atkey(sym-info, sym-key))) } multi method AT-KEY(PseudoStash:D: Str() $key) is raw { my $name := $!package.^name; my Mu $val := nqp::if( (nqp::iseq_s($name, 'CORE') && nqp::iseq_i(nqp::chars($key), 3) && nqp::iseq_i(nqp::index($key, 'v6'), 0)), self!find-rev-core($key), nqp::null()); nqp::if( nqp::isnull($val), nqp::if( nqp::existskey($pseudoers,$key), ($val := nqp::atkey($pseudoers, $key)(self)), nqp::if( (self.REIFY-STORAGE(until-symbol => $key)), nqp::stmts( (my Mu $sym-info := nqp::atkey(nqp::getattr(self, Map, '$!storage'), $key)), ($val := SYM-VALUE($sym-info, $key)))))); nqp::if( nqp::isnull($val), Failure.new(X::NoSuchSymbol.new(symbol => $!package.^name ~ '::<' ~ $key ~ '>')), nqp::if( nqp::istype($val, Exception), Failure.new($val.new(package => $!package.^name, symbol => $key)), $val)) } method BIND-KEY(PseudoStash:D: Str() $key, Mu \value) is raw { nqp::if( nqp::existskey($pseudoers,$key), X::Bind.new(target => "pseudo-package $key").throw, nqp::if( (self.REIFY-STORAGE(until-symbol => $key)), nqp::stmts( (my Mu $storage := nqp::getattr(self, Map, '$!storage')), (my Mu $sym-info := nqp::atkey($storage, $key)), nqp::if( nqp::istype($sym-info, CtxDynThunk), ($sym-info.bindkey($key, value)), nqp::if( nqp::istype($sym-info, Exception), ($sym-info.new(symbol => $key, package => $!package.^name).throw), nqp::bindkey($sym-info, $key, value)))), (X::NoSuchSymbol.new(symbol => $!package.^name ~ '::<' ~ $key ~ '>').throw))) } method EXISTS-KEY(PseudoStash:D: Str:D() $key) { nqp::hllbool( nqp::unless( nqp::existskey($pseudoers, $key), nqp::if( (self.REIFY-STORAGE(until-symbol => $key)), nqp::not_i(nqp::istype(nqp::atkey(nqp::getattr(self, Map, '$!storage'), $key), Exception))))) } my role CtxSymIterator does Rakudo::Iterator::Mappy { has $!implementation-detail; method !set-implementation-detail(:$!implementation-detail) { } method new(PseudoStash:D \stash, $implementation-detail = 0) { # Fill the storage with all available symbols for iteration. stash.REIFY-STORAGE; my $iter = self.Rakudo::Iterator::Mappy::new(stash); # The upstream new may return Rakudo::Iterator::Empty $iter!set-implementation-detail(:$implementation-detail) if $iter ~~ ::?ROLE; $iter } method !accept-item(Mu \iter) { my $sym-info := nqp::iterval(iter); return 0 if nqp::istype($sym-info, Exception); my $val := SYM-VALUE($sym-info, nqp::iterkey_s(iter)); nqp::unless( $!implementation-detail, nqp::not_i( nqp::if( nqp::istype(nqp::decont($val), Code), $val.is-implementation-detail))) } method skip-one { my $iter = nqp::getattr(self, Rakudo::Iterator::Mappy, '$!iter'); my $done = 0; nqp::while( ($iter && !$done), nqp::stmts( nqp::shift($iter), ($done = self!accept-item($iter)))); $done } } my class CtxSymIterator::Pairs does CtxSymIterator { method pull-one { my Mu $item := IterationEnd; nqp::while( ($!iter && nqp::eqaddr($item, IterationEnd)), nqp::stmts( nqp::shift($!iter), nqp::if( self!accept-item($!iter), ($item := Pair.new( nqp::iterkey_s($!iter), SYM-VALUE(nqp::iterval($!iter), nqp::iterkey_s($!iter))))))); $item } method push-all(\target --> IterationEnd) { nqp::while( $!iter, nqp::stmts( nqp::shift($!iter), nqp::if( self!accept-item($!iter), (target.push( Pair.new( nqp::iterkey_s($!iter), SYM-VALUE(nqp::iterval($!iter), nqp::iterkey_s($!iter)))))))) } } my class CtxSymIterator::Keys does CtxSymIterator { method new(|c) { self.CtxSymIterator::new(|c) } method pull-one() { my Mu $item := IterationEnd; nqp::while( ($!iter && nqp::eqaddr($item, IterationEnd)), nqp::stmts( nqp::shift($!iter), nqp::if( (self!accept-item($!iter)), ($item := nqp::iterkey_s($!iter))))); $item } method push-all(\target --> IterationEnd) { nqp::while( $!iter, nqp::stmts( nqp::shift($!iter), nqp::if( (self!accept-item($!iter)), (target.push(nqp::iterkey_s($!iter)))))) } } my class CtxSymIterator::Values does CtxSymIterator { method new(|c) { self.CtxSymIterator::new(|c) } method pull-one { my Mu $item := IterationEnd; nqp::while( ($!iter && nqp::eqaddr($item, IterationEnd)), nqp::stmts( nqp::shift($!iter), nqp::if( self!accept-item($!iter), ($item := SYM-VALUE(nqp::iterval($!iter), nqp::iterkey_s($!iter)))))); $item } method push-all(\target --> IterationEnd) { nqp::while( $!iter, nqp::stmts( nqp::shift($!iter), nqp::if( self!accept-item($!iter), (target.push(SYM-VALUE(nqp::iterval($!iter), nqp::iterkey_s($!iter))))))) } } my class CtxSymIterator::KV does CtxSymIterator { has $!on; method pull-one { my Mu $item = IterationEnd; nqp::while( ($!iter && nqp::eqaddr($item, IterationEnd)), nqp::if( $!on, nqp::stmts( ($!on := 0), ($item := SYM-VALUE(nqp::iterval($!iter), nqp::iterkey_s($!iter)))), nqp::stmts( nqp::shift($!iter), nqp::if( self!accept-item($!iter), nqp::stmts( ($!on := 1), ($item := nqp::iterkey_s($!iter))))))); $item } method push-all(\target --> IterationEnd) { nqp::while( $!iter, nqp::stmts( nqp::shift($!iter), nqp::if( self!accept-item($!iter), nqp::stmts( target.push(nqp::iterkey_s($!iter)), target.push(SYM-VALUE(nqp::iterval($!iter), nqp::iterkey_s($!iter))))))) } } # The default iterator includes the implementation detail symbols. multi method iterator(::?CLASS:D: --> Iterator:D) { CtxSymIterator::Pairs.new(self, 1) } multi method keys(::?CLASS:D: :$implementation-detail --> Seq:D) { Seq.new(CtxSymIterator::Keys.new(self, $implementation-detail)) } multi method values(::?CLASS:D: :$implementation-detail --> Seq:D) { Seq.new(CtxSymIterator::Values.new(self, $implementation-detail)) } multi method kv(::?CLASS:D: :$implementation-detail --> Seq:D) { Seq.new(CtxSymIterator::KV.new(self, $implementation-detail)) } multi method pairs(::?CLASS:D: :$implementation-detail --> Seq:D) { Seq.new(CtxSymIterator::Pairs.new(self, $implementation-detail)) } method sort(::?CLASS:D: :$implementation-detail --> Seq:D) { Seq.new(CtxSymIterator::Pairs.new(self, $implementation-detail)).sort } multi method elems(::?CLASS:D: :$implementation-detail) { Seq.new(CtxSymIterator::Values.new(self, $implementation-detail)).elems } } #line 1 SETTING::src/core.e/Grammar.rakumod my class Grammar is Match { # (oughta be down in Match or even nqp really) method locprepost() { my $orig = self.orig; my $marked = self.?MARKED('ws'); my $pos = $marked && index(" }])>»", substr($orig, self.pos, 1)) < 0 ?? $marked.from !! self.pos; my $prestart = $pos - 40; $prestart = 0 if $prestart < 0; my $pre = substr($orig, $prestart, $pos - $prestart); $pre = $pre.subst(/.*\n/, ""); $pre = '' if $pre eq ''; my $postchars = $pos + 40 > chars($orig) ?? chars($orig) - $pos !! 40; my $post = substr($orig, $pos, $postchars); $post = $post.subst(/\n.*/, ""); $post = '' if $post eq ''; [$pre, $post] } # stolen and trimmed down from World method typed_exception(X::Comp $ex, *%opts) { # If the highwater is beyond the current position, force the cursor to that location. my @expected; my $high = self.'!highwater'(); if $high >= self.pos() { self.'!cursor_pos'($high); my $highexpect := self.'!highexpect'(); if nqp::islist($highexpect) { my %seen; for ^nqp::elems($highexpect) { my $x = nqp::hllizefor(nqp::shift($highexpect),'Raku'); push @expected, $x unless %seen{$x}++; } @expected .= sort; } } my @locprepost := self.locprepost(); # Build and throw exception object. %opts = HLL::Compiler.lineof(self.orig, self.pos, :cache(1)); # only set if it's not already set: %opts //= self.pos; %opts
             = @locprepost[0];
        %opts            = @locprepost[1];
        %opts      = @expected if @expected;
        %opts = 1;
        $ex.new(|%opts).Failure
    }

    method SETFAIL($failed, :$filename) {
        return Nil unless self.defined;
        self.'!cursor_pos'($failed.pos);
        self.typed_exception(X::Syntax::Confused, filename => ($filename // ""))
    }

    method parse(\target, :$rule, :$args, Mu :$actions, :$filename) is raw {
        my $*LINEPOSCACHE;
        nqp::stmts(
          (my $grammar := self.new(:orig(target), |%_).set_actions($actions)),
          nqp::decont(nqp::getlexcaller('$/') =
            nqp::if(
              (my $cursor := nqp::if(
                $rule,
                nqp::if(
                  $args,
                  $grammar."$rule"(|$args.Capture),
                  $grammar."$rule"()
                ),
                nqp::if(
                  $args,
                  $grammar.TOP(|$args.Capture),
                  $grammar.TOP()
                ),
              )),
              nqp::stmts(
                (my $match := $cursor.MATCH),
                nqp::while(
                  $match && nqp::isne_i(
                    nqp::getattr_i(($match := $cursor.MATCH),Match,'$!pos'),
                    target.chars
                  ),
                  $match := ($cursor := $cursor.'!cursor_next'()).MATCH
                ),
                $match || $grammar.SETFAIL($match, :$filename),
              ),
              $grammar.SETFAIL($cursor, :$filename),
            )
          )
        )
    }

    method subparse(\target, :$rule, :$args, :$actions) is raw {
        nqp::stmts(
          (my $grammar := self.new(:orig(target), |%_).set_actions($actions)),
          nqp::decont(nqp::getlexcaller('$/') =
            nqp::if(
              $rule,
              nqp::if(
                $args,
                $grammar."$rule"(|$args.Capture).MATCH,
                $grammar."$rule"().MATCH,
              ),
              nqp::if(
                $args,
                $grammar.TOP(|$args.Capture).MATCH,
                $grammar.TOP().MATCH
              ),
            )
          )
        )
      }

    method parsefile(Str(Cool) $filename, :$enc) is raw {
        nqp::decont(nqp::getlexcaller('$/') = nqp::if(
          nqp::elems(nqp::getattr(%_,Map,'$!storage')),
          self.parse($filename.IO.slurp(:$enc), :$filename, |%_),
          self.parse($filename.IO.slurp(:$enc), :$filename)
        ))
    }
}

#line 1 SETTING::src/core.e/EXPORTHOW.rakumod
# Bind the HOWs into the EXPORTHOW package under the package declarator
# names.

{
    # Don't expose this name in CORE's namespace
    class Perl6::Metamodel::v6e::GrammarHOW
        is Perl6::Metamodel::ClassHOW
        does Perl6::Metamodel::DefaultParent
    {
    }

    # Set 6.e Grammar as the default for grammars
    Perl6::Metamodel::v6e::GrammarHOW.set_default_parent_type(Grammar);
    EXPORTHOW.WHO := Perl6::Metamodel::v6e::GrammarHOW;
}

#line 1 SETTING::src/core.e/array_multislice.rakumod
# all 6.e specific sub postcircumfix [; ] candidates here please

proto sub postcircumfix:<[; ]>($, $, $?, *%) is nodal {*}

# This candidate must be provided because assignment to a multi-level
# hash is codegenned this way.
multi sub postcircumfix:<[; ]>(\SELF, @indices, Mu \assignee) is raw {
    my int $dims = @indices.elems;   # reifies
    my $indices := nqp::getattr(@indices,List,'$!reified');

    my int $i;
    nqp::while(
      nqp::islt_i($i,$dims) && nqp::istype(nqp::atpos($indices,$i),Int),
      ++$i
    );

    nqp::iseq_i($i,$dims)                   # True if all indices are Int
      ?? nqp::iseq_i($dims,2)
        ?? SELF.ASSIGN-POS(
             nqp::atpos($indices,0),
             nqp::atpos($indices,1),
             assignee
           )
        !! nqp::iseq_i($dims,3)
          ?? SELF.ASSIGN-POS(
               nqp::atpos($indices,0),
               nqp::atpos($indices,1),
               nqp::atpos($indices,2),
               assignee
             )
          !! SELF.ASSIGN-POS(|@indices, assignee)
      # need an extra named here to prevent infilooping because otherwise
      # this will code-gen to a call to this candidate again.
      !! (postcircumfix:<[; ]>(SELF, @indices, :none) = assignee)
}

# This candidate must be provided because binding to a multi-level
# hash is codegenned this way.
multi sub postcircumfix:<[; ]>(\SELF, @indices, :$BIND! is raw) is raw {
    my int $dims = @indices.elems;   # reifies
    my $indices := nqp::getattr(@indices,List,'$!reified');

    my int $i;
    nqp::while(
      nqp::islt_i($i,$dims) && nqp::istype(nqp::atpos($indices,$i),Int),
      ++$i
    );

    nqp::iseq_i($i,$dims)                   # True if all indices are Int
      ?? nqp::iseq_i($dims,2)
        ?? SELF.BIND-POS(
             nqp::atpos($indices,0),
             nqp::atpos($indices,1),
             $BIND
           )
        !! nqp::iseq_i($dims,3)
          ?? SELF.BIND-POS(
               nqp::atpos($indices,0),
               nqp::atpos($indices,1),
               nqp::atpos($indices,2),
               $BIND
             )
          !! SELF.BIND-POS(|@indices, $BIND)
      !! X::Bind::Slice.new(type => SELF.WHAT).throw
}

# handle the case of @a[|| 0]
multi sub postcircumfix:<[; ]>(\initial-SELF, \value, *%_) is raw {
    postcircumfix:<[; ]>(initial-SELF, value.List, |%_)
}

# This candidate provides all of the multi-level array access, as well
# as providing the slow-path for assignment of a multi-level array.
multi sub postcircumfix:<[; ]>(\initial-SELF, @indices, *%_) is raw {

    # helper sub to ensure non-assignability
    sub non-assignable(\result) is raw {
        nqp::istype(result,Array)
          ?? result.List
          !! nqp::decont(result)
    }

    # find out what we actually got
    my str $adverbs;
    if nqp::getattr(%_,Map,'$!storage') -> $nameds is raw {
        $adverbs = nqp::atkey($nameds,'exists')
          ?? ":exists"
          !! ":!exists"
          if nqp::existskey($nameds,'exists');

        $adverbs =
               nqp::concat($adverbs,":delete") if nqp::atkey($nameds,'delete');
        $adverbs = nqp::concat($adverbs,":k" ) if nqp::atkey($nameds,'k');
        $adverbs = nqp::concat($adverbs,":kv") if nqp::atkey($nameds,'kv');
        $adverbs = nqp::concat($adverbs,":p" ) if nqp::atkey($nameds,'p');
        $adverbs = nqp::concat($adverbs,":v" ) if nqp::atkey($nameds,'v');
    }

    my int $topdim = @indices.elems;  # .elems reifies
    my $indices   := nqp::getattr(@indices,List,'$!reified');
    my int $i;
    nqp::while(
      nqp::islt_i($i,$topdim) && nqp::istype(nqp::atpos($indices,$i),Int),
      ++$i
    );

    # potential fast paths
    if nqp::iseq_i($i,$topdim) {    # all indices are Ints
        if $adverbs {
            if nqp::iseq_s($adverbs,":delete") {
                return nqp::iseq_i($topdim,2)
                  ?? initial-SELF.EXISTS-POS(
                       nqp::atpos($indices,0),
                       nqp::atpos($indices,1)
                     ) ?? non-assignable(initial-SELF.DELETE-POS(
                            nqp::atpos($indices,0),
                            nqp::atpos($indices,1)
                          ))
                       !! Nil
                  !! nqp::iseq_i($topdim,3)
                    ?? initial-SELF.EXISTS-POS(
                         nqp::atpos($indices,0),
                         nqp::atpos($indices,1),
                         nqp::atpos($indices,2)
                       ) ?? non-assignable(initial-SELF.DELETE-POS(
                              nqp::atpos($indices,0),
                              nqp::atpos($indices,1),
                              nqp::atpos($indices,2)
                            ))
                         !! Nil
                    !! initial-SELF.EXISTS-POS(|@indices)
                      ?? non-assignable(initial-SELF.DELETE-POS(|@indices))
                      !! Nil
            }

            elsif nqp::iseq_s($adverbs,":exists") {
                return nqp::iseq_i($topdim,2)
                  ?? initial-SELF.EXISTS-POS(
                       nqp::atpos($indices,0),
                       nqp::atpos($indices,1)
                     )
                  !! nqp::iseq_i($topdim,3)
                    ?? initial-SELF.EXISTS-POS(
                         nqp::atpos($indices,0),
                         nqp::atpos($indices,1),
                         nqp::atpos($indices,2)
                       )
                    !! initial-SELF.EXISTS-POS(|@indices)
            }

            elsif nqp::iseq_s($adverbs,":!exists") {
                return not nqp::iseq_i($topdim,2)
                  ?? initial-SELF.EXISTS-POS(
                       nqp::atpos($indices,0),
                       nqp::atpos($indices,1)
                     )
                  !! nqp::iseq_i($topdim,3)
                    ?? initial-SELF.EXISTS-POS(
                         nqp::atpos($indices,0),
                         nqp::atpos($indices,1),
                         nqp::atpos($indices,2)
                       )
                    !! initial-SELF.EXISTS-POS(|@indices)
            }
        }

        # fast path without adverbs
        else {
            return-rw nqp::iseq_i($topdim,2)
              ?? initial-SELF.AT-POS(
                   nqp::atpos($indices,0),
                   nqp::atpos($indices,1)
                 )
              !! nqp::iseq_i($topdim,3)
                ?? initial-SELF.AT-POS(
                     nqp::atpos($indices,0),
                     nqp::atpos($indices,1),
                     nqp::atpos($indices,2)
                   )
                !! initial-SELF.AT-POS(|@indices)
        }
    }

    # Did not fast path.  Map $topdim to the highest index number, so that
    # it can be easier used in recursion checks.
    --$topdim;

    # set up standard lexical info for recursing subs
    my \target = nqp::create(IterationBuffer);
    my int $dim;
    my int $return-list;

    if $adverbs {

        if nqp::iseq_s($adverbs,":exists") || nqp::iseq_s($adverbs,":!exists") {
            my $wantnot := nqp::iseq_s($adverbs,":!exists").Bool;

            my sub EXISTS-POS-recursively(\SELF, \idx --> Nil) {
                if nqp::istype(idx, Iterable) && nqp::not_i(nqp::iscont(idx)) {
                    $return-list  = 1;
                    my $iterator := idx.iterator;
                    nqp::until(
                      nqp::eqaddr(
                        (my \pulled := $iterator.pull-one),
                        IterationEnd
                      ),
                      EXISTS-POS-recursively(SELF, pulled)
                    );
                }
                elsif nqp::islt_i($dim,$topdim) {
                    ++$dim;  # going higher
                    if nqp::istype(idx,Whatever) {
                        $return-list  = 1;
                        my \next-idx := nqp::atpos($indices,$dim);
                        my int $i     = -1;
                        my int $elems = SELF.elems;
                        nqp::while(
                          nqp::islt_i(++$i,$elems),
                          EXISTS-POS-recursively(SELF.AT-POS($i), next-idx)
                        );
                    }
                    else  {
                        EXISTS-POS-recursively(
                          SELF.AT-POS(nqp::istype(idx,Callable)
                            ?? (idx.(SELF.elems)).Int
                            !! idx.Int
                          ),
                          nqp::atpos($indices,$dim)
                        );
                    }
                    --$dim;  # done at this level
                }
                # $next-dim == $topdim, reached leaves
                elsif nqp::istype(idx,Whatever) {
                    $return-list  = 1;
                    my int $i     = -1;
                    my int $elems = SELF.elems;
                    nqp::while(
                      nqp::islt_i(++$i,$elems),
                      nqp::push(target,$wantnot ?^ SELF.EXISTS-POS($i))
                    );
                }
                else {
                    nqp::push(
                      target,
                      $wantnot ?^ SELF.EXISTS-POS(nqp::istype(idx,Callable)
                        ?? (idx.(SELF.elems)).Int
                        !! idx.Int
                      )
                    );
                }
            }

            EXISTS-POS-recursively(initial-SELF,nqp::atpos($indices,0));
        }

        elsif nqp::iseq_s($adverbs,":delete") {
            my sub DELETE-POS-recursively(\SELF, \idx --> Nil) {
                if nqp::istype(idx, Iterable) && nqp::not_i(nqp::iscont(idx)) {
                    $return-list  = 1;
                    my $iterator := idx.iterator;
                    nqp::until(
                      nqp::eqaddr(
                        (my \pulled := $iterator.pull-one),
                        IterationEnd
                      ),
                      DELETE-POS-recursively(SELF, pulled)
                    );
                }
                elsif nqp::islt_i($dim,$topdim) {
                    ++$dim;  # going higher
                    if nqp::istype(idx,Whatever) {
                        $return-list  = 1;
                        my \next-idx := nqp::atpos($indices,$dim);
                        my int $i     = -1;
                        my int $elems = SELF.elems;
                        nqp::while(
                          nqp::islt_i(++$i,$elems),
                          DELETE-POS-recursively(SELF.AT-POS($i), next-idx)
                        );
                    }
                    else  {
                        DELETE-POS-recursively(
                          SELF.AT-POS(nqp::istype(idx,Callable)
                            ?? (idx.(SELF.elems)).Int
                            !! idx.Int
                          ),
                          nqp::atpos($indices,$dim)
                        );
                    }
                    --$dim;  # done at this level
                }
                # $next-dim == $topdim, reached leaves
                elsif nqp::istype(idx,Whatever) {
                    $return-list  = 1;
                    my int $i     = -1;
                    my int $elems = SELF.elems;
                    nqp::while(
                      nqp::islt_i(++$i,$elems),
                      nqp::push(
                        target,
                        SELF.EXISTS-POS($i)
                          ?? non-assignable(SELF.DELETE-POS($i))
                          !! Nil
                      )
                    );
                }
                else {
                    my $index := nqp::istype(idx,Callable)
                      ?? (idx.(SELF.elems)).Int
                      !! idx.Int;
                    nqp::push(
                      target,
                      SELF.EXISTS-POS($index)
                        ?? non-assignable(SELF.DELETE-POS($index))
                        !! Nil
                    );
                }
            }

            DELETE-POS-recursively(initial-SELF,nqp::atpos($indices,0));
        }

        # some other combination of adverbs
        else {

            # helper sub to create multi-level keys
            my $keys := nqp::create(IterationBuffer);  # keys encountered
            sub keys-to-list($index) is raw {
                nqp::push((my $list := nqp::clone($keys)),$index);
                $list.List
            }

            # determine the processor to be used
            my &process =
               nqp::iseq_s($adverbs,":exists:delete")
              ?? -> \SELF, \key {
                     SELF.DELETE-POS(key)
                       if nqp::push(target,SELF.EXISTS-POS(key));
                 }
            !! nqp::iseq_s($adverbs,":exists:delete:kv")
              ?? do {
                     $return-list = 1;
                     -> \SELF, \key {
                         if SELF.EXISTS-POS(key) {
                             SELF.DELETE-POS(key);
                             nqp::push(target,keys-to-list(key));
                             nqp::push(target,True);
                         }
                     }
                 }
            !! nqp::iseq_s($adverbs,":exists:delete:p")
              ?? -> \SELF, \key {
                     if SELF.EXISTS-POS(key) {
                         SELF.DELETE-POS(key);
                         nqp::push(
                           target,
                           Pair.new(keys-to-list(key), True)
                        );
                     }
                 }
            !! nqp::iseq_s($adverbs,":exists:kv")
              ?? do {
                     $return-list = 1;
                     -> \SELF, \key {
                         if SELF.EXISTS-POS(key) {
                             nqp::push(target,keys-to-list(key));
                             nqp::push(target,True);
                         }
                     }
                 }
            !! nqp::iseq_s($adverbs,":exists:p")
              ?? -> \SELF, \key {
                     nqp::push(
                       target,
                       Pair.new(keys-to-list(key), True)
                     ) if SELF.EXISTS-POS(key);
                 }
            !! nqp::iseq_s($adverbs,":delete:k")
              ?? -> \SELF, \key {
                     if SELF.EXISTS-POS(key) {
                         SELF.DELETE-POS(key);
                         nqp::push(target,keys-to-list(key));
                     }
                 }
            !! nqp::iseq_s($adverbs,":delete:kv")
              ?? do {
                     $return-list = 1;
                     -> \SELF, \key {
                         if SELF.EXISTS-POS(key) {
                             nqp::push(target,keys-to-list(key));
                             nqp::push(
                               target,
                               non-assignable(SELF.DELETE-POS(key))
                             );
                         }
                     }
                 }
            !! nqp::iseq_s($adverbs,":delete:p")
              ?? -> \SELF, \key {
                     nqp::push(
                       target,
                       Pair.new(
                         keys-to-list(key),
                         non-assignable(SELF.DELETE-POS(key))
                       )
                     ) if SELF.EXISTS-POS(key);
                 }
            !! nqp::iseq_s($adverbs,":delete:v")
              ?? -> \SELF, \key {
                     nqp::push(target,non-assignable(SELF.DELETE-POS(key)))
                       if SELF.EXISTS-POS(key);
                 }
            !! nqp::iseq_s($adverbs,":k")
              ?? -> \SELF, \key {
                     nqp::push(target,keys-to-list(key))
                       if SELF.EXISTS-POS(key);
                 }
            !! nqp::iseq_s($adverbs,":kv")
              ?? do {
                     $return-list = 1;
                     -> \SELF, \key {
                         if SELF.EXISTS-POS(key) {
                             nqp::push(target,keys-to-list(key));
                             nqp::push(target,non-assignable(SELF.AT-POS(key)));
                         }
                     }
                 }
            !! nqp::iseq_s($adverbs,":p")
              ?? -> \SELF, \key {
                     nqp::push(
                       target,
                       Pair.new(
                         keys-to-list(key),
                         non-assignable(SELF.AT-POS(key))
                       )
                     ) if SELF.EXISTS-POS(key);
                 }
            !! nqp::iseq_s($adverbs,":v")
              ?? -> \SELF, \key {
                     nqp::push(target,non-assignable(SELF.AT-POS(key)))
                       if SELF.EXISTS-POS(key);
                 }
            !! return X::Adverb.new(
                 :what,
                 :source(try { initial-SELF.VAR.name } // initial-SELF.^name),
                 :nogo(nqp::split(':',nqp::substr($adverbs,1)))
               ).Failure;

            my sub PROCESS-POS-recursively(\SELF, \idx --> Nil) {
                if nqp::istype(idx,Iterable) && nqp::not_i(nqp::iscont(idx)) {
                    $return-list  = 1;
                    my $iterator := idx.iterator;
                    nqp::until(
                      nqp::eqaddr(
                        (my \pulled := $iterator.pull-one),
                        IterationEnd
                      ),
                      PROCESS-POS-recursively(SELF, pulled)
                    );
                }
                elsif nqp::islt_i($dim,$topdim) {
                    ++$dim;  # going higher
                    if nqp::istype(idx,Whatever) {
                        $return-list  = 1;
                        my \next-idx := nqp::atpos($indices,$dim);
                        my int $i     = -1;
                        my int $elems = SELF.elems;
                        nqp::while(
                          nqp::islt_i(++$i,$elems),
                          nqp::stmts(
                            nqp::push($keys,nqp::clone($i)),
                            PROCESS-POS-recursively(
                              SELF.AT-POS($i),
                              next-idx
                            ),
                            nqp::pop($keys)
                          )
                        );
                    }
                    elsif nqp::istype(idx,Callable) {
                        my $index := (idx.(SELF.elems)).Int;
                        nqp::push($keys,$index),
                        PROCESS-POS-recursively(
                          SELF.AT-POS($index),
                          nqp::atpos($indices,$dim)
                        );
                        nqp::pop($keys)
                    }
                    else  {
                        nqp::push($keys,idx.Int);
                        PROCESS-POS-recursively(
                          SELF.AT-POS(idx.Int), nqp::atpos($indices,$dim)
                        );
                        nqp::pop($keys);
                    }
                    --$dim;  # done at this level
                }
                # $next-dim == $topdim, reached leaves
                elsif nqp::istype(idx,Whatever) {
                    $return-list  = 1;
                    my int $i     = -1;
                    my int $elems = SELF.elems;
                    nqp::while(
                      nqp::islt_i(++$i,$elems),
                      process(SELF, $i)
                    );
                }
                else {
                    process(
                      SELF,
                      nqp::istype(idx,Callable) ?? (idx.(SELF.elems)) !! idx.Int
                    );
                }
            }

            PROCESS-POS-recursively(initial-SELF, nqp::atpos($indices,0));
        }
    }

    # no adverbs whatsoever
    else {
        my sub AT-POS-recursively(\SELF, \idx --> Nil) {
            if nqp::istype(idx,Iterable) && nqp::not_i(nqp::iscont(idx)) {
                $return-list = 1;
                my $iterator := idx.iterator;
                nqp::until(
                  nqp::eqaddr(
                    (my \pulled := $iterator.pull-one),
                    IterationEnd
                  ),
                  AT-POS-recursively(SELF, pulled)
                );
            }
            elsif nqp::islt_i($dim,$topdim) {
                ++$dim;  # going higher
                if nqp::istype(idx,Whatever) {
                    $return-list  = 1;
                    my \next-idx := nqp::atpos($indices,$dim);
                    my int $i     = -1;
                    my int $elems = SELF.elems;
                    nqp::while(
                      nqp::islt_i(++$i,$elems),
                      AT-POS-recursively(SELF.AT-POS($i), next-idx)
                    );
                }
                else  {
                    AT-POS-recursively(
                      SELF.AT-POS(nqp::istype(idx,Callable)
                        ?? (idx.(SELF.elems)).Int
                        !! idx.Int
                      ),
                      nqp::atpos($indices,$dim)
                    );
                }
                --$dim;  # done at this level
            }
            # $next-dim == $topdim, reached leaves
            elsif nqp::istype(idx,Whatever) {
                $return-list  = 1;
                my int $i     = -1;
                my int $elems = SELF.elems;
                nqp::while(
                  nqp::islt_i(++$i,$elems),
                  nqp::push(target,SELF.AT-POS($i))
                );
            }
            else {
                nqp::push(
                  target,
                  SELF.AT-POS(nqp::istype(idx,Callable)
                    ?? (idx.(SELF.elems)).Int
                    !! idx.Int
                  )
                );
            }
        }

        AT-POS-recursively(initial-SELF, nqp::atpos($indices,0));
    }

    # post-process recursive result
    $return-list
      ?? target.List
      !! nqp::elems(target) ?? nqp::atpos(target,0) !! Nil
}

# Can be REMOVED **AFTER** the Raku grammar has become the default grammar
BEGIN &postcircumfix:<[; ]>.set_op_props;

#line 1 SETTING::src/core.e/hash_multislice.rakumod
# all 6.e specific sub postcircumfix {; } candidates here please

proto sub postcircumfix:<{; }>($, $, *%) is nodal {*}

# handle the case of %h{|| "a"}
multi sub postcircumfix:<{; }>(\initial-SELF, \value, *%_) is raw {
    postcircumfix:<{; }>(initial-SELF, value.List, |%_)
}

multi sub postcircumfix:<{; }>(\initial-SELF, @indices,
  :$exists, :$delete, :$k, :$kv, :$p, :$v
) is raw {

    # find out what we actually got
    my str $adverbs;
    $adverbs = $exists ?? ":exists" !! ":!exists" if nqp::isconcrete($exists);
    $adverbs = nqp::concat($adverbs,":delete") if $delete;
    $adverbs = nqp::concat($adverbs,":k")      if $k;
    $adverbs = nqp::concat($adverbs,":kv")     if $kv;
    $adverbs = nqp::concat($adverbs,":p")      if $p;
    $adverbs = nqp::concat($adverbs,":v")      if $v;

    # set up standard lexical info for recursing subs
    my \target   = nqp::create(IterationBuffer);
    my int $dim;
    my int $dims = nqp::sub_i(@indices.elems,1);  # .elems reifies
    my $indices := nqp::getattr(@indices,List,'$!reified');
    my int $return-list;

    if $adverbs {
        if nqp::iseq_s($adverbs,":exists") || nqp::iseq_s($adverbs,":!exists") {
            sub EXISTS-KEY-recursively(\SELF, \idx --> Nil) {
                if nqp::istype(idx, Iterable) && nqp::not_i(nqp::iscont(idx)) {
                    $return-list = 1;
                    my $iterator := idx.iterator;
                    nqp::until(
                      nqp::eqaddr(
                        (my \pulled := $iterator.pull-one),
                        IterationEnd
                      ),
                      EXISTS-KEY-recursively(SELF, pulled)
                    );
                }
                elsif $dim < $dims {
                    ++$dim;  # going higher
                    if nqp::istype(idx,Whatever) {
                        $return-list = 1;
                        my \next-idx := nqp::atpos($indices,$dim);
                        my $iterator := SELF.keys.iterator;
                        nqp::until(
                          nqp::eqaddr(
                            (my \pulled := $iterator.pull-one),
                            IterationEnd
                          ),
                          EXISTS-KEY-recursively(SELF.AT-KEY(pulled), next-idx)
                        );
                    }
                    else  {
                        EXISTS-KEY-recursively(
                          SELF.AT-KEY(idx), nqp::atpos($indices,$dim)
                        );
                    }
                    --$dim;  # done at this level
                }
                # $next-dim == $dims, reached leaves
                elsif nqp::istype(idx,Whatever) {
                    $return-list = 1;
                    my $iterator := SELF.keys.iterator;
                    nqp::until(
                      nqp::eqaddr(
                        (my \pulled := $iterator.pull-one),
                        IterationEnd
                      ),
                      nqp::push(target,SELF.EXISTS-KEY(pulled))
                    );
                }
                else {
                    nqp::push(target,SELF.EXISTS-KEY(idx));
                }
            }

            EXISTS-KEY-recursively(initial-SELF, nqp::atpos($indices,0));

            # negate results if so requested
            unless $exists {
                my int $i     = -1;
                my int $elems = nqp::elems(target);
                nqp::while(
                  nqp::islt_i(++$i,$elems),
                  nqp::bindpos(target,$i,!nqp::atpos(target,$i))
                );
            }
        }

        elsif nqp::iseq_s($adverbs,":delete") {
            sub DELETE-KEY-recursively(\SELF, \idx --> Nil) {
                if nqp::istype(idx, Iterable) && nqp::not_i(nqp::iscont(idx)) {
                    $return-list = 1;
                    my $iterator := idx.iterator;
                    nqp::until(
                      nqp::eqaddr(
                        (my \pulled := $iterator.pull-one),
                        IterationEnd
                      ),
                      DELETE-KEY-recursively(SELF, pulled)
                    );
                }
                elsif $dim < $dims {
                    ++$dim;  # going higher
                    if nqp::istype(idx,Whatever) {
                        $return-list = 1;
                        my \next-idx := nqp::atpos($indices,$dim);
                        my $iterator := SELF.keys.iterator;
                        nqp::until(
                          nqp::eqaddr(
                            (my \pulled := $iterator.pull-one),
                            IterationEnd
                          ),
                          DELETE-KEY-recursively(SELF.AT-KEY(pulled), next-idx)
                        );
                    }
                    else  {
                        DELETE-KEY-recursively(
                          SELF.AT-KEY(idx), nqp::atpos($indices,$dim)
                        );
                    }
                    --$dim;  # done at this level
                }
                # $next-dim == $dims, reached leaves
                elsif nqp::istype(idx,Whatever) {
                    $return-list = 1;
                    my $iterator := SELF.keys.iterator;
                    nqp::until(
                      nqp::eqaddr(
                        (my \pulled := $iterator.pull-one),
                        IterationEnd
                      ),
                      nqp::push(target,SELF.DELETE-KEY(pulled))
                    );
                }
                else {
                    nqp::push(
                      target,
                      SELF.EXISTS-KEY(idx) ?? SELF.DELETE-KEY(idx) !! Nil
                    );
                }
            }

            DELETE-KEY-recursively(initial-SELF, nqp::atpos($indices,0));
        }

        # some other combination of adverbs
        else {

            # helper sub to create multi-level keys
            my $keys := nqp::create(IterationBuffer);  # keys encountered
            sub keys-to-list(\key) {
                nqp::push((my $list := nqp::clone($keys)),key);
                $list.List
            }

            # determine the processor to be used
            my &process =
               nqp::iseq_s($adverbs,":exists:delete")
              ?? -> \SELF, \key {
                     SELF.DELETE-KEY(key)
                       if nqp::push(target,SELF.EXISTS-KEY(key));
                 }
            !! nqp::iseq_s($adverbs,":exists:delete:kv")
              ?? do {
                     $return-list = 1;
                     -> \SELF, \key {
                         if SELF.EXISTS-KEY(key) {
                             SELF.DELETE-KEY(key);
                             nqp::push(target,keys-to-list(key));
                             nqp::push(target,True);
                         }
                     }
                 }
            !! nqp::iseq_s($adverbs,":exists:delete:p")
              ?? -> \SELF, \key {
                     if SELF.EXISTS-KEY(key) {
                         SELF.DELETE-KEY(key);
                         nqp::push(
                           target,
                           Pair.new(keys-to-list(key), True)
                        );
                     }
                 }
            !! nqp::iseq_s($adverbs,":exists:kv")
              ?? do {
                     $return-list = 1;
                     -> \SELF, \key {
                         if SELF.EXISTS-KEY(key) {
                             nqp::push(target,keys-to-list(key));
                             nqp::push(target,True);
                         }
                     }
                 }
            !! nqp::iseq_s($adverbs,":exists:p")
              ?? -> \SELF, \key {
                     nqp::push(
                       target,
                       Pair.new(keys-to-list(key), True)
                     ) if SELF.EXISTS-KEY(key);
                 }
            !! nqp::iseq_s($adverbs,":delete:k")
              ?? -> \SELF, \key {
                     if SELF.EXISTS-KEY(key) {
                         SELF.DELETE-KEY(key);
                         nqp::push(target,keys-to-list(key));
                     }
                 }
            !! nqp::iseq_s($adverbs,":delete:kv")
              ?? do {
                     $return-list = 1;
                     -> \SELF, \key {
                         if SELF.EXISTS-KEY(key) {
                             nqp::push(target,keys-to-list(key));
                             nqp::push(target,SELF.DELETE-KEY(key));
                         }
                     }
                 }
            !! nqp::iseq_s($adverbs,":delete:p")
              ?? -> \SELF, \key {
                     nqp::push(
                       target,
                       Pair.new(keys-to-list(key), SELF.DELETE-KEY(key))
                     ) if SELF.EXISTS-KEY(key);
                 }
            !! nqp::iseq_s($adverbs,":delete:v")
              ?? -> \SELF, \key {
                     nqp::push(target,SELF.DELETE-KEY(key))
                       if SELF.EXISTS-KEY(key);
                 }
            !! nqp::iseq_s($adverbs,":k")
              ?? -> \SELF, \key {
                     nqp::push(target,keys-to-list(key))
                       if SELF.EXISTS-KEY(key);
                 }
            !! nqp::iseq_s($adverbs,":kv")
              ?? do {
                     $return-list = 1;
                     -> \SELF, \key {
                         if SELF.EXISTS-KEY(key) {
                             nqp::push(target,keys-to-list(key));
                             nqp::push(target,nqp::decont(SELF.AT-KEY(key)));
                         }
                     }
                 }
            !! nqp::iseq_s($adverbs,":p")
              ?? -> \SELF, \key {
                     nqp::push(
                       target,
                       Pair.new(keys-to-list(key), SELF.AT-KEY(key))
                     ) if SELF.EXISTS-KEY(key);
                 }
            !! nqp::iseq_s($adverbs,":v")
              ?? -> \SELF, \key {
                     nqp::push(target,nqp::decont(SELF.AT-KEY(key)))
                       if SELF.EXISTS-KEY(key);
                 }
            !! return X::Adverb.new(
                 :what,
                 :source(try { initial-SELF.VAR.name } // initial-SELF.^name),
                 :nogo(nqp::split(':',nqp::substr($adverbs,1)))
               ).Failure;

            sub PROCESS-KEY-recursively(\SELF, \idx --> Nil) {
                if nqp::istype(idx,Iterable) && nqp::not_i(nqp::iscont(idx)) {
                    $return-list = 1;
                    my $iterator := idx.iterator;
                    nqp::until(
                      nqp::eqaddr(
                        (my \pulled := $iterator.pull-one),
                        IterationEnd
                      ),
                      PROCESS-KEY-recursively(SELF, pulled)
                    );
                }
                elsif $dim < $dims {
                    ++$dim;  # going higher
                    if nqp::istype(idx,Whatever) {
                        $return-list = 1;
                        my $iterator := SELF.keys.iterator;
                        my \next-idx := nqp::atpos($indices,$dim);
                        nqp::until(
                          nqp::eqaddr(
                            (my \pulled := $iterator.pull-one),
                            IterationEnd
                          ),
                          nqp::stmts(
                            nqp::push($keys,pulled),
                            PROCESS-KEY-recursively(
                              SELF.AT-KEY(pulled),
                              next-idx
                            ),
                            nqp::pop($keys)
                          )
                        );
                    }
                    else  {
                        nqp::push($keys,idx);
                        PROCESS-KEY-recursively(
                          SELF.AT-KEY(idx), nqp::atpos($indices,$dim)
                        );
                        nqp::pop($keys);
                    }
                    --$dim;  # done at this level
                }
                # $next-dim == $dims, reached leaves
                elsif nqp::istype(idx,Whatever) {
                    $return-list = 1;
                    my $iterator := SELF.keys.iterator;
                    nqp::until(
                      nqp::eqaddr(
                        (my \pulled := $iterator.pull-one),
                        IterationEnd
                      ),
                      process(SELF, pulled)
                    );
                }
                else {
                    process(SELF, idx);
                }
            }

            PROCESS-KEY-recursively( initial-SELF, nqp::atpos($indices,0));
        }
    }

    # no adverbs whatsoever
    else {
        my int $non-deterministic;

        sub AT-KEY-recursively(\SELF, \idx --> Nil) {
            if nqp::istype(idx,Iterable) && nqp::not_i(nqp::iscont(idx)) {
                $return-list = 1;
                my $iterator := idx.iterator;
                nqp::until(
                  nqp::eqaddr((my \pulled := $iterator.pull-one),IterationEnd),
                  AT-KEY-recursively(SELF, pulled)
                );
            }
            elsif $dim < $dims {
                $dim++;  # going higher
                if nqp::istype(idx,Whatever) {
                    $return-list = 1;
                    my \next-idx := nqp::atpos($indices,$dim);
                    my $iterator := SELF.keys.iterator;
                    $non-deterministic = 1 unless $iterator.is-deterministic;
                    nqp::until(
                      nqp::eqaddr(
                        (my \pulled := $iterator.pull-one),
                        IterationEnd
                      ),
                      AT-KEY-recursively(SELF.AT-KEY(pulled), next-idx)
                    );
                }
                else  {
                    AT-KEY-recursively(
                      SELF.AT-KEY(idx), nqp::atpos($indices,$dim)
                    );
                }
                --$dim;  # done at this level
            }
            # $next-dim == $dims, reached leaves
            elsif nqp::istype(idx,Whatever) {
                $return-list = 1;
                my $iterator := SELF.keys.iterator;
                $non-deterministic = 1 unless $iterator.is-deterministic;
                nqp::until(
                  nqp::eqaddr((my \pulled := $iterator.pull-one),IterationEnd),
                  nqp::push(target,SELF.AT-KEY(pulled))
                );
            }
            else {
                nqp::push(target,SELF.AT-KEY(idx));
            }
        }

        AT-KEY-recursively(initial-SELF, nqp::atpos($indices,0));

        # decont all elements if non-deterministic to disallow assignment
        if $non-deterministic {
            my int $i     = -1;
            my int $elems = nqp::elems(target);
            nqp::while(
              nqp::islt_i(++$i,$elems),
              nqp::bindpos(target,$i,nqp::decont(nqp::atpos(target,$i)))
            );
        }
    }

    $return-list
      ?? target.List
      !! nqp::elems(target) ?? nqp::atpos(target,0) !! Nil
}

# Can be REMOVED **AFTER** the Raku grammar has become the default grammar
BEGIN &postcircumfix:<{; }>.set_op_props;

#line 1 SETTING::src/core.e/Formatter/Grammar.rakumod
#-------------------------------------------------------------------------------
# The grammar for parsing format strings.  Intentionally globally visible
# to allow the ecosystem to subclass it if necessary

grammar Formatter::Syntax {
    token TOP { ^ * $ }

    method panic($message, $payload) {
        my $ex := nqp::newexception();
        nqp::setmessage($ex, $message);
        nqp::setpayload($ex, $payload);
        nqp::throw($ex);
    }

    token statement {
        [
        |  [ 
          || <.panic(
            "'"
              ~ self.orig.substr(1)
              ~ "' is not valid in sprintf format sequence '"
              ~ self.orig
              ~ "'",
            nqp::hash(
              'BAD_DIRECTIVE',
              nqp::hash(
                'DIRECTIVE', self.orig.substr(1),
                'SEQUENCE', self.orig
              )
            )
          )> ]
        |  
        ]
    }

    proto token directive { <...> }
    token directive:sym {
        '%' ? * ? ? $=<[bB]>
    }
    token directive:sym {
        '%' ? * ? 
    }
    token directive:sym {
        '%' ? * ? ? $=<[dDi]>
    }
    token directive:sym {
        '%' ? * ? ? $=<[eE]>
    }
    token directive:sym {
        '%' ? * ? ? $=<[fF]>
    }
    token directive:sym {
        '%' ? * ? ? $=<[gG]>
    }
    token directive:sym {
        '%' ? * ? ? 
    }
    token directive:sym {
        '%' ? * ? ? 
    }
    token directive:sym {
        '%' ? * ? ? 
    }
    token directive:sym {
        '%' ? * ? 
    }
    token directive:sym {
        '%' ? * ? ? $=<[xX]>
    }
    token directive:sym<%> { '%%' }

    token literal { <-[%]>+ }

    token idx { [\d+] '$' }

    token flags { <[\ +0#-]> }

    token size { \d* | $='*' ? }

    token precision { '.' ? }
}

#line 1 SETTING::src/core.e/Formatter.rakumod
# THEORY OF OPERATION
# -------------------
# The new method will look for the given format in a hash.  If found,
# it contains a Callable that will process the parameters doing all of the
# necessary checks and conversions.
#
# If not found, it will parse the format with the Formatter:: Syntax grammar
# and the Formatter::Actions class, which will create the RakuAST nodes for
# it and store the Callable in the hash.
#
# TODO:
# - generate code that uses native ops and variables where possible

# problem cases that should be checked:
# - https://github.com/Raku/old-issue-tracker/issues/4537
#   say sprintf('%f %f %f %f', Mu, Any, Nil, NaN);
# - https://github.com/Raku/old-issue-tracker/issues/4892
#   say sprintf("%e",1000)    # should be 1.0... instead of 10....

our class Formatter {

#-------------------------------------------------------------------------------
# Subroutines referenced at runtime by the generated ASTs, to reduce the
# actual size of the specific parts of sprintf processing.

    # pad with zeroes as integer
    our sub pad-zeroes-int(int $positions, Str:D $string) {
        nqp::isgt_i($positions,0)
          ?? nqp::islt_i(nqp::chars($string),$positions)
            ?? nqp::eqat($string,'-',0)
              ?? nqp::concat('-',pad-zeroes-str(
                   nqp::sub_i($positions,1),nqp::substr($string,1)
                 ))
              !! pad-zeroes-str($positions,$string)
            !! $string
          !! $string
    }

    # pad with zeroes after decimal point
    our sub pad-zeroes-precision(int $positions, Str:D $string) {
        my int $index = nqp::index($string,'.');
        if nqp::isge_i($index,0) {
            my int $digits =  # $string.chars - 1 - $index;
              nqp::sub_i(nqp::sub_i(nqp::chars($string),1),$index);

            nqp::isgt_i($positions,$digits)
              ?? nqp::concat(
                   $string,
                   nqp::x('0',nqp::sub_i($positions,$digits))
                 )
              !! $string
        }
        else {
            $positions
              ?? nqp::concat('.',nqp::x('0',$positions))
              !! $string
        }
    }

    # pad with zeroes as string
    our sub pad-zeroes-str(int $positions, Str:D $string) {
        nqp::islt_i(nqp::chars($string),$positions)
          ?? nqp::concat(
               nqp::x('0',nqp::sub_i($positions,nqp::chars($string))),
               $string
             )
          !! $string
    }

    # prefix given hash properly, also if value negative
    our sub prefix-hash(str $hash, Str:D $string) {
        nqp::eqat($string,'-',0)
          ?? nqp::concat('-',nqp::concat($hash,nqp::substr($string,1)))
          !! nqp::concat($hash,$string)
    }

    # prefix plus if value is not negative
    our sub prefix-plus(Str:D $string) {
        nqp::eqat($string,'-',0)
          ?? $string
          !! nqp::concat("+",$string)
    }

    # prefix space if string not starting with "+" or "-"
    our sub prefix-space(Str:D $string) {
        nqp::eqat($string,'-',0) || nqp::eqat($string,'+',0)
          ?? $string
          !! nqp::concat(' ',$string)
    }

    # prefix 0 if string not starting with 0
    our sub prefix-zero(Str:D $string) {
        nqp::eqat($string,'0',0)
          ?? $string
          !! nqp::eqat($string,'-',0)
            ?? nqp::eqat($string,'0',1)
              ?? $string
              !! nqp::concat('-0',nqp::substr($string,1))
            !! nqp::concat('0',$string)
    }

    # set up value for scientific notation
    our proto sub scientify(|) {*}
    our multi sub scientify($letter, $positions, $value --> Str:D) {
        scientify($letter, $positions, $value.Numeric)
    }
    our multi sub scientify($letter, $positions, Numeric:D $value --> Str:D) {
        if $value {
            my $exponent := $value ?? $value.abs.log(10).floor !! 0;
            my $abs-expo := $exponent.abs;
            pad-zeroes-precision(
              $positions,
              ($value / 10 ** $exponent).round(10**-$positions).Str
            ) ~ $letter
              ~ ($exponent < 0 ?? "-" !! "+")
              ~ ($abs-expo < 10 ?? "0" ~ $abs-expo !! $abs-expo)
        }
        else {
            "0." ~ nqp::x("0",$positions) ~ $letter ~ "+00"
        }
    }

    # provide left justification of string
    our sub str-left-justified(int $positions, Str:D $string) {
        nqp::islt_i(nqp::chars($string),nqp::abs_i($positions))
          ?? nqp::concat(
               $string,nqp::x(
                 ' ',nqp::sub_i(nqp::abs_i($positions),nqp::chars($string))
               )
             )
          !! $string
    }

    # provide right justification of string
    our sub str-right-justified(int $positions, Str:D $string) {
        nqp::islt_i($positions,0)
          ?? str-left-justified($positions, $string)
          !! nqp::islt_i(nqp::chars($string),$positions)
            ?? nqp::concat(
                 nqp::x(' ',nqp::sub_i($positions,nqp::chars($string))),
                 $string
               )
            !! $string
    }

#-------------------------------------------------------------------------------
# Actions class to be used with Grammar to turn format into array of piecesi
# of code and have the results joined into a single string.

    my class Actions {

#-------------------------------------------------------------------------------
# Helper subroutines that generate RakuAST::Nodes.  These all have the "ast"
# prefix and should only be called at format parse time.  These methods are
# agnostic of the grammar / actions.

        # helper sub to call a method on a given AST
        proto sub ast-call-method(|) {*}
        multi sub ast-call-method($ast, $name --> RakuAST::ApplyPostfix:D) {
            RakuAST::ApplyPostfix.new(
              operand => $ast,
              postfix => RakuAST::Call::Method.new(
                name => RakuAST::Name.from-identifier($name)
              )
            )
        }
        multi sub ast-call-method(
          $ast, $name, $one
        --> RakuAST::ApplyPostfix:D) {
            RakuAST::ApplyPostfix.new(
              operand => $ast,
              postfix => RakuAST::Call::Method.new(
                name => RakuAST::Name.from-identifier($name),
                args => RakuAST::ArgList.new($one)
              )
            )
        }
        multi sub ast-call-method(
          $ast, $name, $one, $two
        --> RakuAST::ApplyPostfix:D) {
            RakuAST::ApplyPostfix.new(
              operand => $ast,
              postfix => RakuAST::Call::Method.new(
                name => RakuAST::Name.from-identifier($name),
                args => RakuAST::ArgList.new($one, $two)
              )
            )
        }

        # helper sub to call a sub with the given parameters
        proto sub ast-call-sub(|) {*}
        multi sub ast-call-sub($name, $one --> RakuAST::Call::Name:D) {
            RakuAST::Call::Name.new(
              name => RakuAST::Name.from-identifier-parts('Formatter', $name),
              args => RakuAST::ArgList.new($one)
            )
        }
        multi sub ast-call-sub($name, $one, $two --> RakuAST::Call::Name:D) {
            RakuAST::Call::Name.new(
              name => RakuAST::Name.from-identifier-parts('Formatter', $name),
              args => RakuAST::ArgList.new($one, $two)
            )
        }
        multi sub ast-call-sub(
          $name, $one, $two, $three
        --> RakuAST::Call::Name:D) {
            RakuAST::Call::Name.new(
              name => RakuAST::Name.from-identifier-parts('Formatter', $name),
              args => RakuAST::ArgList.new($one, $two, $three)
            )
        }

        # helper sub to call an infix operator
        sub ast-infix($left, $infix, $right --> RakuAST::ApplyInfix:D) {
            RakuAST::ApplyInfix.new(
              left  => $left,
              infix => RakuAST::Infix.new($infix),
              right => $right
            )
        }

        # helper sub for creating literal integer nodes
        sub ast-integer(Int:D $int --> RakuAST::IntLiteral:D) {
            RakuAST::IntLiteral.new($int)
        }

        # helper sub to call a prefix operator
        sub ast-prefix($prefix, $operand --> RakuAST::ApplyPrefix:D) {
            RakuAST::ApplyPrefix.new(
              prefix  => RakuAST::Prefix.new($prefix),
              operand => $operand
            )
        }

        # helper sub for creating literal string nodes
        sub ast-string(Str:D $string --> RakuAST::StrLiteral:D) {
            RakuAST::StrLiteral.new($string)
        }

        # helper sub to create a ternary
        sub ast-ternary($condition, $then, $else --> RakuAST::Ternary) {
            RakuAST::Ternary.new(:$condition, :$then, :$else)
        }

#-------------------------------------------------------------------------------
# Helper subs that obtain information from Match objects.  These always take
# $/ as the first positional parameter.

        # helper sub to check if a flag is set
        sub has-hash($/)  { "#" (elem) $.map: *.Str }
        sub has-minus($/) { "-" (elem) $.map: *.Str }
        sub has-plus($/)  { "+" (elem) $.map: *.Str }
        sub has-space($/) { " " (elem) $.map: *.Str }
        sub has-zero($/)  { "0" (elem) $.map: *.Str }


        # helper sub to get size specification
        sub size($/ --> RakuAST::Node:D) { any-size($) }

        # helper sub to get precision specification
        sub precision($/ --> RakuAST::Node:D) { any-size($) }

        # helper sub to get any size-type info.  Note that this will "eat"
        # parameters if a * is specified, indicating runtime width info.
        sub any-size($/ --> RakuAST::Node:D) {
            $/
              ?? $
                ?? ast-call-method(parameter($/), 'Int')
                !! (my $size := $/.Int) > 1
                  ?? ast-integer($size)
                  !! Nil
              !! Nil
        }

        # helper sub to determine the value for this directive
        sub parameter($/ --> RakuAST::Node:D) {
            my Int $index = $ ?? $.chop.Int !! $*NEXT-PARAMETER;
            X::Str::Sprintf::Directives::Unsupported.new(
              directive => ~$,
              sequence  => ~$/,
            ).throw if $index < 1;

            # set default index for next parameter
            $*NEXT-PARAMETER = $index + 1;

            # record the directive, * indicates a position indicator (e.g. 4$)
            @*DIRECTIVES[$index] = .Str with $ // '*';

            my $letter = "a";
            $letter++ while --$index;
            RakuAST::Var::Lexical.new('$' ~ $letter)
        }

        # helper sub for float values handling plus/minus/zero padding
        sub plus-minus-zero($/, $size, $ast is copy) {

            if has-plus($/) {
                # prefix-plus($ast)
                $ast = ast-call-sub('prefix-plus', $ast);
            }

            if $size {
                # justification($size, $ast)
                $ast = ast-call-sub(
                  has-minus($/)
                    ?? 'str-left-justified'
                    !! has-zero($/)
                      ?? "pad-zeroes-int"
                      !! "str-right-justified",
                  $size,
                  $ast
                );
            }

            $ast
        }

        # Helper sub to obtain size / precision / parameter ASTs.
        # We first need to get any size/precision information because
        # they can be parameter based and should be specified *before*
        # the actual argument
        proto sub spa(|) {*}
        multi sub spa($/ --> List:D) {
            (size($/), precision($/), parameter($/))
        }
        multi sub spa($/, Int:D $default --> List:D) {
            (size($/), precision($/) // ast-integer($default), parameter($/))
        }

        # helper sub for processing formats for integer values
        sub handle-integer-numeric($/,
           Int :$base,    # the number base to assume for generating string
           Str :$hash,    # the string to prefix if "#" is in format
           Str :$coerce,  # method name to initially coerce with, default Int
          Bool :$plus,    # whether to prefix "+" if positive
          Bool :$space,   # whether to prefix " " if not starting with + or -
          Bool :$lc       # whether to lowercase resulting string
        ) {
            my ($size is copy, $precision is copy, $parameter) := spa($/);

            if !$precision && $size {
                if has-zero($/) && !has-minus($/) {
                    $precision := $size;
                    $size      := Nil;
                }
#                else {
#                    $precision = literal-integer(1);
#                }
            }

            # $a.Int
            my $ast := ast-call-method($parameter, $coerce // 'Int');

            # $ast.(Str || .base($base))
            $ast := $base && $base != 10
              ?? ast-call-method($ast, 'base', ast-integer($base))
              !! ast-call-method($ast, 'Str');

            # $ast.lc
            $ast := ast-call-method($ast, 'lc') if $lc;

            # handle any prefixes
            my int $minus;
            if $hash && has-hash($/) {
                if $hash eq '0' {   # only for octal
                    # prefix-zero($ast)
                    $ast  := ast-call-sub('prefix-zero', $ast);
                    $minus = 1;
                }
                else {
                    # parameter ?? prefix-hash('$hash',$ast) !! $ast
                    $ast := ast-ternary(
                      $parameter,
                      ast-call-sub('prefix-hash', ast-string($hash), $ast),
                      $ast
                    );
                    $minus = $hash.chars;
                }
            }

            my $prefix;
            if $plus && has-plus($/) {
                $prefix = 'prefix-plus';
                $minus  = 1;
            }
            elsif $space && has-space($/) {
                $prefix = 'prefix-space';
                $minus  = 1;
            }

            # expand to precision indicated
            if $precision {
                my $width := $prefix
                  # $precision - 1
                  ?? ast-infix($precision, "-", ast-integer(1))
                  !! $precision;

                # pad-zeroes-int(
                #   $parameter ?? ($precision - $minus) !! $precision, $ast
                # )
                $ast := ast-call-sub(
                  'pad-zeroes-int',
                  ast-ternary(
                    $parameter,
                    $minus
                      ?? ast-infix($width, "-", ast-integer($minus))
                      !! $width,
                    $width
                  ),
                  $ast
                );
            }

            # $prefix($ast)
            $ast := ast-call-sub($prefix, $ast) if $prefix;

            # handle justification only if we need to
            if $size {
                # str-(left|right)-justified($precision, $ast)
                $ast := ast-call-sub(
                  has-minus($/)
                    ?? "str-left-justified"
                    !! "str-right-justified",
                  $size,
                  $ast
                );
            }

            # Set up special handling of 0 if 0 precision
            if !$precision && $ {
                # parameter ?? $ast !! ""
                $ast := ast-ternary($parameter, $ast, ast-string(""));
            }

            $ast
        }

#-------------------------------------------------------------------------------
# These are the actual action methods that will be called when the associated
# token in the grammar matches.

        # collect all the statements made
        method statement($/ --> Nil){
            make ($ || $).made;
        }

        # any non-format related string
        method literal($/ --> Nil) {
            make ast-string($/.Str);
        }

        # show numeric value in binary
        method directive:sym($/ --> Nil) {
            make handle-integer-numeric(
              $/, :base(2), :plus, :space, :hash("0$")
            );
        }

        # show character representation of codepoint value
        method directive:sym($/ --> Nil) {
            my ($size, $precision, $parameter) := spa($/);

            # $a.chr
            my $ast := ast-call-method($parameter, 'chr');

            if $size {
                # str-(left|right)-justified($size, $ast)
                $ast := ast-call-sub(
                  has-minus($/)
                    ?? "str-left-justified"
                    !! has-zero($/)
                      ?? "pad-zeroes-str"
                      !! "str-right-justified",
                  $size,
                  $ast
                );
            }

            make $ast;
        }

        # show decimal (integer) value
        method directive:sym($/ --> Nil) {
            make handle-integer-numeric($/, :base(10), :plus, :space);
        }

        # show floating point value, scientific notation
        method directive:sym($/ --> Nil) {
            my ($size, $precision, $parameter) := spa($/, 6);

            # scientify($precision,'e',$a)
            my $ast := ast-call-sub(
              'scientify', ast-string($.Str), $precision, $parameter
            );

            make plus-minus-zero($/, $size, $ast);
        }

        # show floating point value
        method directive:sym($/ --> Nil) {
            my ($size, $precision, $parameter) := spa($/, 6);

            # $a.Numeric
            my $ast := ast-call-method($parameter, 'Numeric');

            # $ast.round(10 ** -$precision)
            $ast := ast-call-method(
              $ast,
              'round',
              ast-infix(ast-integer(10), '**', ast-prefix('-', $precision))
            );

            # $ast.Str
            $ast := ast-call-method($ast, 'Str');

            make plus-minus-zero($/, $size, $ast);
        }

        # f or e depending on value
        method directive:sym($/ --> Nil) {
            self."directive:sym"($/);  # for now
        }

        # show numeric value in octal using Perl / Raku semantics
        method directive:sym($/ --> Nil) {
            make handle-integer-numeric($/, :base(8), :hash<0>);
        }

        # show string
        method directive:sym($/ --> Nil) {
            my ($size, $precision, $parameter) := spa($/);

            # make sure we have a (potentially truncated) string
            my $ast := $precision
                 # $a.substr(0,$precision)
              ?? ast-call-method(
                   $parameter, 'substr', ast-integer(0), $precision
                 )
                 # $a.Str
              !! ast-call-method($parameter, 'Str');

            # perform any justification
            $ast := ast-call-sub(
              'str-' ~ (has-minus($/) ?? 'left' !! 'right') ~ '-justified',
              $size,
              $ast
            ) if $size;

            make $ast;
        }

        # show unsigned decimal (integer) value
        method directive:sym($/ --> Nil) {
            make handle-integer-numeric($/, :plus, :space, :coerce);
        }

        # show numeric value in hexadecimal
        method directive:sym($/ --> Nil) {
            make handle-integer-numeric(
              $/, :base(16), :hash("0$"), :lc($ eq "x")
            )
        }

        # an escaped %
        method directive:sym<%>($/ --> Nil) {
            make ast-string('%');
        }
    }

#-------------------------------------------------------------------------------
# The actual AST generation logic

    method AST(Str(Cool) $format) {

        # If we don't have a DIRECTIVES array yet, create one and call
        # ourselves again, now *with* the DIRECTIVES array being available.
        # This allows an external caller (such as Format.new) to set up
        # their own DIRECTIVES array and so be able to find out what the
        # directives were.
        if nqp::istype(@*DIRECTIVES,Failure) {
            my @*DIRECTIVES := my str @;  # the directives seen
            return &?ROUTINE(self, $format);
        }
        @*DIRECTIVES.unshift("");       # we're 1-based internally

        # Index of next parameter to be expected.  Note that we do this
        # 1-based rather than 0-based, for easier matching with position
        # specifications, which *are* 1-based.
        my $*NEXT-PARAMETER = 1;

        if Formatter::Syntax.parse($format, actions => Actions) -> $parsed {
            my @operands = $parsed.map: *.made;

            # at least one directive
            @*DIRECTIVES.shift;  # 0-based from now on
            if @*DIRECTIVES -> @directives {

                # set up the statements
                my $stmts := RakuAST::StatementList.new(
                  RakuAST::Statement::Expression.new(
                    expression => @operands == 1
                      ?? @operands.head  # already stringified
                      !! RakuAST::ApplyPostfix.new(
                           operand => RakuAST::ApplyListInfix.new(
                             infix    => RakuAST::Infix.new(','),
                             operands => @operands
                           ),
                          postfix => RakuAST::Call::Method.new(
                            name => RakuAST::Name.from-identifier('join')
                          )
                        )
                  )
                );

                # set up the parameters list
                my $letter = "a";
                my @parameters = (^@directives).map: {
                    RakuAST::Parameter.new(
                      target => RakuAST::ParameterTarget::Var.new(
                        '$' ~ $letter++
                      )
                    )
                }

                # -> $a, $b, ... { $ast }
                RakuAST::PointyBlock.new(
                  signature => RakuAST::Signature.new(
                    parameters => @parameters.List
                  ),
                  body      => RakuAST::Blockoid.new($stmts)
                );
            }

            # no directives, just a string
            else {
                RakuAST::PointyBlock.new(
                  body => RakuAST::Blockoid.new(
                    RakuAST::StatementList.new(
                      RakuAST::Statement::Expression.new(:expression(
                        # If there are no directives, it generally is just
                        # the format string, with one exception: if there
                        # are '%%' escapes.  Instead of picking @parts apart
                        # then, just escape the '%%' here string-wise.
                        RakuAST::StrLiteral.new($format.subst('%%','%',:g))
                      ))
                    )
                  )
                )
            }
        }
        else {
            die "huh?"
        }
    }

    # Return Callable for given format
    method CODE(Str(Cool) $format --> Callable:D) {
        self.AST($format).EVAL
    }

    # actual workhorse for sprintf()
    my $FORMATS := nqp::hash;  # where we keep our formats
    method new(Str:D $format) {
        nqp::ifnull(
          nqp::atkey($FORMATS,$format),
          self!fetch-new-format($format)
        )
    }

    # Threadsafe cache updater, don't care about multiple threads trying
    # to do the same format, but this way we don't have a lock if the
    # same format is called *many* times over
    method !fetch-new-format(Str:D $format) {
        my $new := nqp::clone($FORMATS);

        # remove the first key we encounter if max reached
        nqp::deletekey($new,nqp::iterkey_s(nqp::shift(nqp::iterator($new))))
          if nqp::isge_i(nqp::elems($new),100);  # XXX  should be settable

        nqp::bindkey($new,$format,my $code := self.CODE($format));
        $FORMATS := $new;
        $code
    }
}

#line 1 SETTING::src/core.e/Format.rakumod
#-------------------------------------------------------------------------------
# The class underlying the format string quote q:o/:format type.  An instance
# contains a Callable that will be called with the CALL-ME method.
# Otherwise acts as a normal string.

my class Format is Str {
    has str @!directives;
    has     &!code;

    method new(Str:D $format, :$class = Formatter) {
        my @*DIRECTIVES := my str @;
        my &code := $class.new($format);

        my $obj := nqp::create(self);
        nqp::bindattr_s($obj,Str,'$!value',$format);
        nqp::bindattr($obj,Format,'@!directives',@*DIRECTIVES);
        nqp::bindattr($obj,Format,'&!code',&code);
        $obj
    }

    method directives(Format:D:) { @!directives.List      }
    method Callable(Format:D:)   { &!code                 }
    method arity(Format:D:)      { &!code.signature.arity }
    method count(Format:D:)      { &!code.signature.count }

    method CALL-ME(|c) {
        CATCH {
            die ("Error occurred during processing format '"
              ~ self
              ~ "' with value"
              ~ (c == 1 ?? " " !! "s ")
              ~ c.raku.substr(1)
              ~ ":"
            ).naive-word-wrapper
              ~ "\n\n"
              ~ .message.indent(4)
        }

        &!code(|c)
    }

    # Helper method for .fmt support
    method handle-iterator(Format:D:
      Iterator:D $iterator, $separator = "\n"
    --> Str:D) is implementation-detail {
        my &handler  := &!code;

        # at least one arg required for format
        if self.arity -> int $arity {
            my str @parts;
            if $arity == 1 {
                nqp::until(
                  nqp::eqaddr((my $pulled := $iterator.pull-one),IterationEnd),
                  nqp::push_s(@parts,handler($pulled))
                );
            }

            # 2 or more args required
            else {

                # collect values for args, throw if insufficient
                my sub next-batch() {
                    my $buffer  := nqp::create(IterationBuffer);
                    my int $todo = $arity + 1;
                    nqp::while(
                      --$todo,
                      nqp::if(
                        nqp::eqaddr(
                          (my $pulled := $iterator.pull-one),
                          IterationEnd
                        ),
                        ($todo = 1),
                        nqp::push($buffer,$pulled)
                      )
                    );
                    
                    my int $found = nqp::elems($buffer);
                    $found
                      ?? $found == $arity
                        ?? $buffer.List
                        !! self!throw-arity(nqp::elems($buffer), $arity)
                      !! Nil
                }

                nqp::while(
                  (my $params := next-batch),
                  nqp::push_s(@parts,handler(|$params))
                );
            }

            @parts.join($separator);
        }

        # no args, throw if any values
        else {
            nqp::eqaddr($iterator.pull-one,IterationEnd)
              ?? ''
              !! self!throw-arity(0, 1)
        }
    }

    # helper for throwing
    method !throw-arity($args-have, $args-used) is hidden-from-backtrace {
        X::Str::Sprintf::Directives::Count.new(
          :$args-have, :$args-used, :format(self)
        ).throw
    }

    multi method raku(Format:D:) { 'Format.new(' ~ self.Str::raku ~ ')' }

    # mostly for debugging, but also for consistency
    method AST(Format:D:) { Formatter.AST: self }
}

#-------------------------------------------------------------------------------
# Subroutine access to Formatter logic

# the procedural frontend of sprintf functionality
multi sub sprintf(Format:D $format, *@args) {  # until zprintf gone
    $format(|@args)
}

proto sub zprintf($, |) {*}
multi sub zprintf(Str(Cool) $format, \value) {
    Formatter.new($format)(value)
}
multi sub zprintf(Str(Cool) $format, |c) {
    Formatter.new($format)(|c)
}

augment class Cool {
    method zprintf(|c) { zprintf(self,|c) }
}

#line 1 SETTING::src/core.e/Rakudo/Iterator.rakumod
augment class Rakudo::Iterator {
    # Return an iterator that generates Lists of a given iterator
    # and one or Callables with conditions.  This is basically the
    # Haskell "span" functionality.
    my class Snip does Iterator {
        has $!tests;   # Iterator producing smartmatch targets
        has $!values;  # Iterator producing values
        has $!next;    # next value to produce between pull-one calls

        method !SET-SELF($!tests, $!values) {
            $!next := $!values.pull-one;
            self
        }

        method new($tests, $values) {
            nqp::create(self)!SET-SELF($tests, $values)
        }

        method pull-one() {
            if nqp::eqaddr($!next,IterationEnd) {
                IterationEnd
            }
            else {
                my $buffer := nqp::create(IterationBuffer);
                nqp::push($buffer,$!next);

                my $values := $!values;
                my $test;
                my $pulled;

                # no more tests, produce the rest
                if nqp::eqaddr(($test := $!tests.pull-one),IterationEnd) {
                    $!next := IterationEnd;
                    nqp::bindattr((my \result := nqp::create(List)),
                      List,'$!reified',$buffer);
                    nqp::bindattr((my \todo := nqp::create(List::Reifier)),
                      List::Reifier,'$!reified',$buffer);
                    nqp::bindattr(todo,
                      List::Reifier,'$!current-iter',$values);
                    nqp::bindattr(todo,
                      List::Reifier,'$!reification-target',$buffer);
                    nqp::p6bindattrinvres(result,List,'$!todo',todo)
                }

                # produce matching this test
                else {
                    nqp::until(
                      nqp::eqaddr(($pulled := $values.pull-one),IterationEnd)
                        || nqp::isfalse($test.ACCEPTS($pulled)),
                      nqp::push($buffer,$pulled)
                    );
                    $!next := $pulled;
                    $buffer.List
                }
            }
        }
    }

    proto method Snip(|) {*}
    multi method Snip(Iterator:D $tests, Iterator:D $values) {
        Snip.new: $tests, $values
    }

    # Return an iterator that will skip / produce values as specified by
    # another iterator.
    my class Skipper does Iterator {
        has $!values;
        has $!skips;
        has uint $!produce;

        method !SET-SELF($values, $skips) {
            if nqp::eqaddr((my $produce := $skips.pull-one),IterationEnd) {
                $values  # nothing to skip
            }
            else {
                $!values := $values;
                $!skips  := $skips;
                $!produce = $produce;
                self
            }
        }

        method new($values, $skips) {
            nqp::create(self)!SET-SELF($values, $skips)
        }

        method pull-one() {

            # Still something to produce, so produce
            if $!produce {
                --$!produce;
                $!values.pull-one
            }

            # Skip rest if no skipper or *
            elsif nqp::eqaddr((my $skipper := $!skips.pull-one),IterationEnd)
              || nqp::istype($skipper,Whatever) {
                IterationEnd
            }

            # Skip a number
            else {
                my uint $to-skip = $skipper;
                nqp::until(
                  nqp::isle_i($to-skip,0)
                    || nqp::eqaddr($!values.pull-one,IterationEnd),
                  --$to-skip
                );

                # not enough values to skip, so we're done
                if $to-skip {
                    IterationEnd
                }

                # find out how many values to produce next
                else {
                    $!produce = nqp::eqaddr(
                      (my $produce := $!skips.pull-one),
                      IterationEnd
                    ) || nqp::istype($produce,Whatever)
                      ?? -1  # produce the rest
                      !! $produce - 1;
                    $!values.pull-one
                }
            }
        }
    }

    proto method Skipper(|) {*}
    multi method Skipper(\values,  \skips) { Skipper.new: values, skips }
}

#line 1 SETTING::src/core.e/Fixups.rakumod
# This file contains fixups to existing core classes by means of augmentation
# for language level 6.e.

augment class Any {

    # introducing snip
    proto method snip(|) {*}
    multi method snip(Any:D: \condition) {
        Seq.new: Rakudo::Iterator.Snip(condition.iterator, self.iterator)
    }
    multi method snip(Any:D: @conditions) {
        Seq.new: Rakudo::Iterator.Snip(@conditions.iterator, self.iterator)
    }
    multi method snip(Any:D: *@conditions) {
        Seq.new: Rakudo::Iterator.Snip(@conditions.iterator, self.iterator)
    }

    multi method skip(Iterable:D $skips) {
        Seq.new: Rakudo::Iterator.Skipper: self.iterator, $skips.iterator
    }
    multi method skip(*@skips) {
        self.skip(@skips)
    }

    # introducing snitch
    proto method snitch(|) {*}
    multi method snitch(Seq:D \SNITCHEE: &snitcher = ¬e) is raw {
        snitcher SNITCHEE.cache;
        SNITCHEE
    }
    multi method snitch(\SNITCHEE: &snitcher = ¬e) is raw {
        snitcher SNITCHEE;
        SNITCHEE
    }
}

#-------------------------------------------------------------------------------
augment class Bag {

    # add support for Format formats
    multi method fmt(Bag:D: Format:D $format, $separator = "\n" --> Str:D) {
        $format.handle-iterator:
          ($format.count == 1 ?? self.keys !! self.kv).iterator, $separator
    }
}

#-------------------------------------------------------------------------------
augment class BagHash {

    # add support for Format formats
    multi method fmt(BagHash:D: Format:D $format, $separator = "\n" --> Str:D) {
        $format.handle-iterator:
          ($format.count == 1 ?? self.keys !! self.kv).iterator, $separator
    }
}

#-------------------------------------------------------------------------------
augment class Complex {

    # handle sign correctly
    method sign(Complex:D: --> Complex:D) {
        $_ == 0 ?? 0i !! self / $_ given self.abs;
    }
}

#-------------------------------------------------------------------------------
augment class Int {

    # handle negative sqrts being Complex
    multi method sqrt(Int:D:) is default {
        nqp::islt_I(self,0)
          ?? Complex.new(
               0,
               nqp::p6box_n(nqp::sqrt_n(nqp::abs_n(nqp::tonum_I(self))))
             )
          !! nqp::p6box_n(nqp::sqrt_n(nqp::tonum_I(self)))
    }

    # allow 42.roll to be short for (^42).roll
    proto method roll(|) {*}
    multi method roll() { nqp::rand_I(self,Int) }
    multi method roll($count) { (^self).roll($count) }

    # allow 42.pick to be short for (^42).pick
    proto method pick(|) {*}
    multi method pick() { nqp::rand_I(self,Int) }
    multi method pick($count) { (^self).pick($count) }
}

#-------------------------------------------------------------------------------
augment class List {

    # add support for Format formats
    multi method fmt(List:D: Format:D $format, $separator = ' ' --> Str:D) {
        self.is-lazy
          ?? self.fail-iterator-cannot-be-lazy('.fmt',"")
          !! $format.handle-iterator: self.iterator, $separator
    }
}

#-------------------------------------------------------------------------------
augment class Map {

    # add support for Format formats
    multi method fmt(Map:D: Format:D $format, $separator = "\n" --> Str:D) {
        $format.handle-iterator:
          ($format.count == 1 ?? self.keys !! self.kv).iterator, $separator
    }
}

#-------------------------------------------------------------------------------
augment class Mix {

    # add support for Format formats
    multi method fmt(Mix:D: Format:D $format, $separator = "\n" --> Str:D) {
        $format.handle-iterator:
          ($format.count == 1 ?? self.keys !! self.kv).iterator, $separator
    }
}

#-------------------------------------------------------------------------------
augment class MixHash {

    # add support for Format formats
    multi method fmt(MixHash:D: Format:D $format, $separator = "\n" --> Str:D) {
        $format.handle-iterator:
          ($format.count == 1 ?? self.keys !! self.kv).iterator, $separator
    }
}

#-------------------------------------------------------------------------------
augment class Num {

    # handle negative logs being Complex
    multi method log(Num:D:) is default {
        nqp::islt_n(self,0e0)
          ?? Complex.new(
               nqp::p6box_n(nqp::log_n(nqp::abs_n(nqp::unbox_n(self)))),
               pi
             )
          !! nqp::p6box_n(nqp::log_n(nqp::unbox_n(self)));
    }

    # handle negative sqrts being Complex
    multi method sqrt(Num:D:) is default {
        nqp::islt_n(self,0e0)
          ?? Complex.new(
               0,
               nqp::p6box_n(nqp::sqrt_n(nqp::abs_n(nqp::unbox_n(self))))
             )
          !! nqp::p6box_n(nqp::sqrt_n(nqp::unbox_n(self)));
    }
}

#-------------------------------------------------------------------------------
augment class Pair {

    # add support for Format formats
    multi method fmt(Pair:D: Format:D $format --> Str:D) {
        $format($!key, $!value)
    }
}

#-------------------------------------------------------------------------------
augment class Range {

    # handle Range.Bool correctly
    multi method Bool(Range:D: --> Bool:D) {
        nqp::hllbool($!is-int
          ?? ($!max - $!excludes-max - $!min - $!excludes-min) > -1
          !! nqp::not_i(nqp::eqaddr(self.iterator.pull-one,IterationEnd))
        )
    }
}

#-------------------------------------------------------------------------------
augment class Seq {

    # add support for Format formats
    multi method fmt(Seq:D: Format:D $format, $separator = ' ' --> Str:D) {
        self.is-lazy
          ?? self.fail-iterator-cannot-be-lazy('.fmt',"")
          !! $format.handle-iterator: self.iterator, $separator
    }
}

#-------------------------------------------------------------------------------
augment class Set {

    # Add support for Format formats.   Note that the invocant is marked
    # as Setty rather than Set, because this will serve as the handler
    # for the SetHash class.  Sadly, it is not possible to augment roles,
    # otherwise the Setty role itself would have been augmented.
    multi method fmt(Set:D: Format:D $format, $separator = "\n" --> Str:D) {
        $format.handle-iterator:
          ($format.count == 1 ?? self.keys !! self.kv).iterator, $separator
    }
}

#-------------------------------------------------------------------------------
augment class SetHash {

    # add support for Format formats
    multi method fmt(SetHash:D: Format:D $format, $separator = "\n" --> Str:D) {
        $format.handle-iterator:
          ($format.count == 1 ?? self.keys !! self.kv).iterator, $separator
    }
}

#-------------------------------------------------------------------------------
augment class Str {

    # introduce rotor-like capabilities to comb
    multi method comb(Str:D: Pair:D $what, $limit = *, :$partial) {
        my int $size = $what.key;
        my int $step = $size + $what.value;
        $step = 1 if $step < 1;
        $size <= 1 && (nqp::istype($limit,Whatever) || $limit == Inf)
          ?? self.comb
          !! Seq.new:
               Rakudo::Iterator.NGrams: self, $size, $limit, $step, $partial

    }
}

#-------------------------------------------------------------------------------
augment class Supply {

    # introducing snip
    proto method snip($, |) {*}
    multi method snip(Supply:D: $test) {
        self.snip( ($test,) )
    }

    multi method snip(Supply:D: @tests) {
        my @left    = @tests;
        my $test   := @left ?? @left.shift !! Nil;
        my $buffer := nqp::create(IterationBuffer);
        supply {
            whenever self -> \val {
                if nqp::eqaddr($test,Nil) {
                    nqp::push($buffer,val);
                }
                elsif $test.ACCEPTS(val) {
                    emit $buffer.List;
                    nqp::push(($buffer := nqp::create(IterationBuffer)),val);
                    $test := @left ?? @left.shift !! Nil;
                }
                else {
                    nqp::push($buffer,val);
                }
                LAST {
                    emit $buffer.List;
                }
            }
        }
    }

    multi method snip(Supply:D: *@tests) {
        self.snip(@tests)
    }
}

#line 1 SETTING::src/core.e/additions.rakumod
# This file contains additions to the CORE:: namespace for language level 6.e.
# This could be either as additional multi sub candidates, or new subs / terms
# altogether.

# introducing rotor-like capabilities to comb
multi sub comb(Pair:D $rotor, Cool:D $input, *%_) { $input.comb($rotor, |%_) }

# introducing nano as an alternetive to "time"
sub term:() { nqp::time }

# allow next/last to produce a value
multi sub next(\x --> Nil) { THROW(nqp::const::CONTROL_NEXT, x) }
multi sub last(\x --> Nil) { THROW(nqp::const::CONTROL_LAST, x) }

# introducing //foo as syntax for foo.defined
proto sub prefix:($) is pure {*}
multi sub prefix:(\a) { a.defined }

# Can be REMOVED **AFTER** the Raku grammar has become the default grammar
BEGIN &prefix:.set_op_props;

# introducing rotor as a sub
proto sub rotor(|) {*}
multi sub rotor(Int:D $batch, \thing, *%_) {
    thing.rotor($batch, |%_)
}
# We have to emulate :(*@ [@list, \tail]) because the grammar wont cut it.
multi sub rotor(**@cycle-and-thing, *%_) {
    @cycle-and-thing.tail.rotor(@cycle-and-thing.head(*-1), |%_)
}

# introducing snip as a sub
proto sub snip($, |) {*}
multi sub snip(\condition,  +values) { values.snip(condition)  }
multi sub snip(@conditions, +values) { values.snip(@conditions) }

# introducing snitch as a sub
proto sub snitch($, |) {*}
multi sub snitch(Seq:D \SNITCHEE) is raw { note SNITCHEE.cache; SNITCHEE }
multi sub snitch(      \SNITCHEE) is raw { note SNITCHEE;       SNITCHEE }

multi sub snitch(&snitcher, Seq:D \SNITCHEE) is raw {
    snitcher SNITCHEE.cache;
    SNITCHEE
}
multi sub snitch(&snitcher, \SNITCHEE) is raw {
    snitcher SNITCHEE;
    SNITCHEE
}


# vim: set ft=perl6 nomodifiable :