diff --git a/src/Perl6/Actions.nqp b/src/Perl6/Actions.nqp index 6915e1813ae..0cb918beeb5 100644 --- a/src/Perl6/Actions.nqp +++ b/src/Perl6/Actions.nqp @@ -2170,17 +2170,14 @@ class Perl6::Actions is HLL::Actions does STDActions { if $*LABEL { $loop.push(QAST::WVal.new( :value($world.find_single_symbol($*LABEL)), :named('label') )); } - # Handle phasers. - my $code := $loop[1].ann('code_object'); - my $block_type := $world.find_single_symbol_in_setting('Block'); - my $phasers := nqp::getattr($code, $block_type, '$!phasers'); - if !nqp::ishash($phasers) { - $loop[1] := pblock_immediate($loop[1]); - } - else { + # Handle any loopy phasers. + my $code := $loop[1].ann('code_object'); + my $Block := $world.find_single_symbol_in_setting('Block'); + my $phasers := nqp::getattr($code, $Block, '$!phasers'); + if nqp::ishash($phasers) { my $node := $loop.node; if nqp::existskey($phasers, 'NEXT') { - my $phascode := $world.run_phasers_code($code, $loop[1], $block_type, 'NEXT'); + my $phascode := $world.run_phasers_code($code, $loop[1], $Block, 'NEXT'); if +@($loop) == 2 { $loop.push($phascode); } @@ -2203,9 +2200,12 @@ class Perl6::Actions is HLL::Actions does STDActions { } if nqp::existskey($phasers, 'LAST') { $loop := QAST::Stmts.new(:$node, :resultchild(0), $loop, - $world.run_phasers_code: $code, $loop[1], $block_type, 'LAST'); + $world.run_phasers_code: $code, $loop[1], $Block, 'LAST'); } } + else { # no phasers or a lone LEAVE phaser + $loop[1] := pblock_immediate($loop[1]); + } $loop } @@ -4405,8 +4405,10 @@ class Perl6::Actions is HLL::Actions does STDActions { method maybe_add_inlining_info($/, $code, $sig, $past, @params) { # Cannot inline things with custom invocation handler or phasers. return 0 if nqp::can($code, 'CALL-ME'); - my $phasers := nqp::getattr($code,$*W.find_single_symbol_in_setting('Block'),'$!phasers'); - return 0 unless !nqp::ishash($phasers) || !$phasers; + + return 0 if nqp::isconcrete(nqp::getattr( + $code, $*W.find_single_symbol_in_setting('Block'), '$!phasers' + )); # Make sure the block has the common structure we expect # (decls then statements). @@ -4416,7 +4418,7 @@ class Perl6::Actions is HLL::Actions does STDActions { # them. No parameters also means no inlining. return 0 unless @params; - my $world := $*W; + my $world := $*W; my $Param := $world.find_single_symbol_in_setting('Parameter'); my @p_objs := nqp::getattr($sig, $world.find_single_symbol_in_setting('Signature'), '@!params'); my %arg_placeholders; diff --git a/src/Perl6/Optimizer.nqp b/src/Perl6/Optimizer.nqp index bd70f743630..a4d3822e7aa 100644 --- a/src/Perl6/Optimizer.nqp +++ b/src/Perl6/Optimizer.nqp @@ -3895,7 +3895,7 @@ class Perl6::Optimizer { ?? nqp::getattr($code, $block, '$!phasers') !! nqp::null(); if $count == 1 - && !nqp::ishash($phasers) + && !nqp::isconcrete($phasers) && %range_bounds{$c2.name}($c2) -> @fls { if $reverse { my $tmp := @fls[0]; diff --git a/src/Perl6/World.nqp b/src/Perl6/World.nqp index a9ae4c78458..3f368b9d3d8 100644 --- a/src/Perl6/World.nqp +++ b/src/Perl6/World.nqp @@ -2739,23 +2739,24 @@ class Perl6::World is HLL::World { # Adds any extra code needing for handling phasers. method add_phasers_handling_code($code, $code_past) { - my $block_type := self.find_single_symbol_in_setting('Block'); - if nqp::istype($code, $block_type) { - my %phasers := nqp::getattr($code, $block_type, '$!phasers'); - if nqp::ishash(%phasers) { + my $Block := self.find_single_symbol_in_setting('Block'); + if nqp::istype($code, $Block) { + my $phasers := nqp::getattr($code, $Block, '$!phasers'); + if nqp::ishash($phasers) { + my %phasers := $phasers; if nqp::existskey(%phasers, 'PRE') { $code_past[0].push(QAST::Op.new( :op('p6setpre') )); - $code_past[0].push(self.run_phasers_code($code, $code_past, $block_type, 'PRE')); + $code_past[0].push(self.run_phasers_code($code, $code_past, $Block, 'PRE')); $code_past[0].push(QAST::Op.new( :op('p6clearpre') )); } if nqp::existskey(%phasers, 'FIRST') { $code_past[0].push(QAST::Op.new( :op('if'), QAST::Op.new( :op('p6takefirstflag') ), - self.run_phasers_code($code, $code_past, $block_type, 'FIRST'))); + self.run_phasers_code($code, $code_past, $Block, 'FIRST'))); } if nqp::existskey(%phasers, 'ENTER') { - $code_past[0].push(self.run_phasers_code($code, $code_past, $block_type, 'ENTER')); + $code_past[0].push(self.run_phasers_code($code, $code_past, $Block, 'ENTER')); } if nqp::existskey(%phasers, '!LEAVE-ORDER') || nqp::existskey(%phasers, 'POST') { $code_past.has_exit_handler(1); @@ -2771,6 +2772,9 @@ class Perl6::World is HLL::World { )); } } + elsif nqp::isconcrete($phasers) { # lone LEAVE phaser + $code_past.has_exit_handler(1); + } } } diff --git a/src/Perl6/bootstrap.c/BOOTSTRAP.nqp b/src/Perl6/bootstrap.c/BOOTSTRAP.nqp index 2c6f29a4741..96f9e25d941 100644 --- a/src/Perl6/bootstrap.c/BOOTSTRAP.nqp +++ b/src/Perl6/bootstrap.c/BOOTSTRAP.nqp @@ -2312,9 +2312,8 @@ BEGIN { nqp::setcodeobj($do_cloned, $cloned); #?if !jvm my $phasers := nqp::getattr($dcself, Block, '$!phasers'); - if nqp::isconcrete($phasers) { - $dcself."!clone_phasers"($cloned, $phasers); - } + $dcself."!clone_phasers"($cloned, $phasers) + if nqp::ishash($phasers); #?endif my $compstuff := nqp::getattr($dcself, Code, '@!compstuff'); unless nqp::isnull($compstuff) { @@ -2383,10 +2382,10 @@ BEGIN { #?endif })); Block.HOW.add_method(Block, '!capture_phasers', nqp::getstaticcode(sub ($self) { - my $dcself := nqp::decont($self); + my $dcself := nqp::decont($self); #?if !jvm - my $phasers := nqp::getattr($dcself, Block, '$!phasers'); - if nqp::isconcrete($phasers) { + my $phasers := nqp::getattr($dcself, Block, '$!phasers'); + if nqp::ishash($phasers) { my @next := nqp::atkey($phasers, 'NEXT'); if nqp::islist(@next) { my int $i := -1; @@ -4055,93 +4054,114 @@ nqp::sethllconfig('Raku', nqp::hash( }, 'exit_handler', -> $coderef, $resultish { unless nqp::p6inpre() { - my %phasers := - nqp::getattr(nqp::getcodeobj($coderef),Block,'$!phasers'); - my @leaves := nqp::atkey(%phasers, '!LEAVE-ORDER'); - my @posts := nqp::atkey(%phasers, 'POST'); - my @exceptions; - unless nqp::isnull(@leaves) { - - # only have a single LEAVEish phaser, so no frills needed - if nqp::elems(@leaves) == 1 && nqp::elems(%phasers) == 1 { + # when we get here, we assume the $!phasers attribut is concrete. + # if it is *not* a hash, it is a lone LEAVE phaser, the most + # commenly used phaser (in the core at least). + my $phasers := nqp::getattr( + nqp::getcodeobj($coderef),Block,'$!phasers' + ); + + # slow path here + if nqp::ishash($phasers) { + my @leaves := nqp::atkey($phasers, '!LEAVE-ORDER'); + my @posts := nqp::atkey($phasers, 'POST'); + my @exceptions; + unless nqp::isnull(@leaves) { + my @keeps := nqp::atkey($phasers, 'KEEP'); + my @undos := nqp::atkey($phasers, 'UNDO'); + my int $n := nqp::elems(@leaves); + my int $i := -1; + + # fast leave path + if nqp::isnull(@leaves) && nqp::isnull(@undos) { + while ++$i < $n { + CATCH { nqp::push(@exceptions, $_) } #?if jvm - nqp::decont(nqp::atpos(@leaves,0))(); + nqp::atpos(@leaves, $i))(); #?endif #?if !jvm - nqp::p6capturelexwhere( - nqp::decont(nqp::atpos(@leaves,0)).clone)(); + nqp::p6capturelexwhere( + nqp::atpos(@leaves, $i).clone() + )(); #?endif - # don't bother to CATCH, there can only be one exception - } + } + } - # slow path here - else { - my @keeps := nqp::atkey(%phasers, 'KEEP'); - my @undos := nqp::atkey(%phasers, 'UNDO'); - my int $n := nqp::elems(@leaves); - my int $i := -1; - my int $run; - my $phaser; - while ++$i < $n { - $phaser := nqp::decont(nqp::atpos(@leaves, $i)); - $run := 1; - unless nqp::isnull(@keeps) { - for @keeps { - if nqp::eqaddr(nqp::decont($_),$phaser) { - $run := !nqp::isnull($resultish) && - nqp::isconcrete($resultish) && - $resultish.defined; - last; + # slow leave paths + else { + my int $run; + my $phaser; + while ++$i < $n { + $phaser := nqp::atpos(@leaves, $i); + $run := 1; + unless nqp::isnull(@keeps) { + for @keeps { + if nqp::eqaddr($_,$phaser) { + $run := !nqp::isnull($resultish) && + nqp::isconcrete($resultish) && + $resultish.defined; + last; + } } } - } - unless nqp::isnull(@undos) { - for @undos { - if nqp::eqaddr(nqp::decont($_),$phaser) { - $run := nqp::isnull($resultish) || - !nqp::isconcrete($resultish) || - !$resultish.defined; - last; + unless nqp::isnull(@undos) { + for @undos { + if nqp::eqaddr($_,$phaser) { + $run := nqp::isnull($resultish) || + !nqp::isconcrete($resultish) || + !$resultish.defined; + last; + } } } - } - if $run { + if $run { + CATCH { nqp::push(@exceptions, $_) } #?if jvm - $phaser(); + $phaser(); #?endif #?if !jvm - nqp::p6capturelexwhere($phaser.clone())(); + nqp::p6capturelexwhere($phaser.clone())(); #?endif - CATCH { nqp::push(@exceptions, $_) } + } } } } - } - unless nqp::isnull(@posts) { - my $value := nqp::ifnull($resultish,Mu); - my int $n := nqp::elems(@posts); - my int $i := -1; - while ++$i < $n { + unless nqp::isnull(@posts) { + my $value := nqp::ifnull($resultish,Mu); + my int $n := nqp::elems(@posts); + my int $i := -1; + while ++$i < $n { #?if jvm - nqp::atpos(@posts, $i)($value); + nqp::atpos(@posts, $i)($value); #?endif #?if !jvm - nqp::p6capturelexwhere(nqp::atpos(@posts,$i).clone)($value); + nqp::p6capturelexwhere(nqp::atpos(@posts,$i).clone)($value); #?endif - CATCH { nqp::push(@exceptions, $_); last; } + CATCH { nqp::push(@exceptions, $_); last; } + } } - } - if @exceptions { - if nqp::elems(@exceptions) > 1 { - Perl6::Metamodel::Configuration.throw_or_die( - 'X::PhaserExceptions', - "Multiple exceptions were thrown by LEAVE/POST phasers", - :exceptions(@exceptions) - ); + if @exceptions { + nqp::elems(@exceptions) > 1 + ?? Perl6::Metamodel::Configuration.throw_or_die( + 'X::PhaserExceptions', + "Multiple exceptions were thrown by LEAVE/POST phasers", + :exceptions(@exceptions) + ) + !! nqp::rethrow(@exceptions[0]); } - nqp::rethrow(@exceptions[0]); + } + + # only have a lone LEAVE phaser, so no frills needed + # don't bother to CATCH, there can only be one exception + else { +#?if jvm + $phasers(); +#?endif +#?if !jvm + nqp::p6capturelexwhere($phasers.clone)(); +#?endif } } }, diff --git a/src/core.c/Block.pm6 b/src/core.c/Block.pm6 index 99071db17d4..b647ecc98cc 100644 --- a/src/core.c/Block.pm6 +++ b/src/core.c/Block.pm6 @@ -1,4 +1,3 @@ -my class PhasersList is repr('VMArray') {} my class Block { # declared in BOOTSTRAP # class Block is Code # has Mu $!phasers; @@ -10,21 +9,63 @@ my class Block { # declared in BOOTSTRAP method returns(Block:D:) { nqp::getattr(self,Code,'$!signature').returns } - method add_phaser(str $name, &block --> Nil) { - $!phasers := nqp::hash unless nqp::ishash($!phasers); - - nqp::bindkey($!phasers,$name,nqp::create(PhasersList)) - unless nqp::existskey($!phasers,$name); + # These methods cannot be private methods as private method dispatch is + # not working yet this early in the setting. + method unshift-phaser(str $name, &block --> Nil) is implementation-detail { + nqp::unshift( + nqp::ifnull( + nqp::atkey($!phasers,$name), + nqp::bindkey($!phasers,$name,nqp::create(IterationBuffer)) + ), + &block + ) + } + method push-phaser(str $name, &block --> Nil) is implementation-detail { + nqp::push( + nqp::ifnull( + nqp::atkey($!phasers,$name), + nqp::bindkey($!phasers,$name,nqp::create(IterationBuffer)) + ), + &block + ) + } - if nqp::iseq_s($name,'LEAVE') || nqp::iseq_s($name,'KEEP') || nqp::iseq_s($name,'UNDO') { - nqp::unshift(nqp::atkey($!phasers,$name),&block); - self.add_phaser('!LEAVE-ORDER', &block); + method add_phaser(str $name, &block --> Nil) { + # $!phasers is either a Block (which indicates the fast path for + # handling an only LEAVE phaser), or a hash (indicating one or more + # other phasers) or not concrete (no phasers at all). + + # adding another phaser after a lone LEAVE phaser? + if nqp::isconcrete($!phasers) && nqp::not_i(nqp::ishash($!phasers)) { + my &leave := $!phasers; + $!phasers := nqp::hash; + self.unshift-phaser('!LEAVE-ORDER', &leave); + self.unshift-phaser('LEAVE', &leave); } - elsif nqp::iseq_s($name,'NEXT') || nqp::iseq_s($name,'!LEAVE-ORDER') || nqp::iseq_s($name,'POST') { - nqp::unshift(nqp::atkey($!phasers,$name),&block); + + # NOTE: nqp::iseq_s is needed as it is too early in the setting to + # have eq work on native strings + if nqp::iseq_s($name,'LEAVE') { + if nqp::isconcrete($!phasers) { + self.unshift-phaser('!LEAVE-ORDER', &block); # slow leaving + self.unshift-phaser($name, &block); # introspection + } + else { + $!phasers := █ # fast path for an only LEAVE phaser + } } else { - nqp::push(nqp::atkey($!phasers,$name),&block); + $!phasers := nqp::hash unless nqp::isconcrete($!phasers); + + if nqp::iseq_s($name,'KEEP') || nqp::iseq_s($name,'UNDO') { + self.unshift-phaser('!LEAVE-ORDER', &block); + self.unshift-phaser($name, &block); + } + else { + nqp::iseq_s($name,'NEXT') || nqp::iseq_s($name,'POST') + ?? self.unshift-phaser($name, &block) + !! self.push-phaser($name, &block); + } } } @@ -32,48 +73,57 @@ my class Block { # declared in BOOTSTRAP # Block. Returns Nil if there are no phasers, the only phaser if # there only is one, or a Callable that will call all of the phasers. method callable_for_phaser(str $name) { - nqp::if( - nqp::ishash($!phasers) - && (my \phasers := nqp::atkey($!phasers,$name)), - nqp::if( - nqp::iseq_i(nqp::elems(phasers),1), - nqp::atpos(phasers,0), - { + nqp::ishash($!phasers) && (my \blocks := nqp::atkey($!phasers,$name)) + ?? nqp::iseq_i(nqp::elems(blocks),1) + ?? nqp::atpos(blocks,0) + !! { + my int $i = -1; + nqp::while( + ++$i < nqp::elems(blocks), + nqp::atpos(blocks,$i)(), + :nohandler + ); + } + !! nqp::isconcrete($!phasers) && nqp::iseq_s($name,'LEAVE') + ?? $!phasers # lone LEAVE phaser + !! Nil + } + + method fire_if_phasers(str $name --> Nil) { + if nqp::isconcrete($!phasers) { + if nqp::ishash($!phasers) + && nqp::atkey($!phasers,$name) -> \blocks { my int $i = -1; nqp::while( - nqp::islt_i(++$i,nqp::elems(phasers)), - nqp::atpos(phasers,$i)(), + ++$i < nqp::elems(blocks), + nqp::atpos(blocks,$i)(), :nohandler ); } - ), - Nil - ) - } - - method fire_if_phasers(str $name --> Nil) { - if nqp::ishash($!phasers) && nqp::atkey($!phasers,$name) -> \phasers { - my int $i = -1; - nqp::while( - nqp::islt_i(++$i,nqp::elems(phasers)), - nqp::atpos(phasers,$i)(), - :nohandler - ); + elsif nqp::iseq_s($name,'LEAVE') { + $!phasers(); # lone LEAVE phaser + } } } method fire_phasers(str $name --> Nil) { - my \phasers := nqp::atkey($!phasers,$name); - my int $i = -1; - nqp::while( - nqp::islt_i(++$i,nqp::elems(phasers)), - nqp::atpos(phasers,$i)(), - :nohandler - ); + if nqp::ishash($!phasers) { + if nqp::atkey($!phasers,$name) -> \blocks { + my int $i = -1; + nqp::while( + ++$i < nqp::elems(blocks), + nqp::atpos(blocks,$i)(), + :nohandler + ); + } + } + elsif nqp::isconcrete($!phasers) && nqp::iseq_s($name,'LEAVE') { + $!phasers(); + } } method has-phasers() { - nqp::hllbool(nqp::ishash($!phasers)) + nqp::hllbool(nqp::isconcrete($!phasers)) } method has-loop-phasers() { nqp::hllbool( @@ -87,16 +137,19 @@ my class Block { # declared in BOOTSTRAP method has-phaser(str $name) { nqp::hllbool( - nqp::ishash($!phasers) && nqp::existskey($!phasers,$name) + (nqp::ishash($!phasers) && nqp::existskey($!phasers,$name)) + || (nqp::iseq_s($name,'LEAVE') && nqp::isconcrete($!phasers)) ) } method phasers(str $name) { nqp::ishash($!phasers) - && nqp::existskey($!phasers,$name) - ?? nqp::p6bindattrinvres(nqp::create(List),List,'$!reified', - nqp::atkey($!phasers,$name)) - !! () + ?? nqp::existskey($!phasers,$name) + ?? nqp::atkey($!phasers,$name).List + !! () + !! nqp::iseq_s($name,'LEAVE') && nqp::isconcrete($!phasers) + ?? ($!phasers,) + !! () } multi method raku(Block:D:) { diff --git a/src/core.c/Variable.pm6 b/src/core.c/Variable.pm6 index 84533a60bf5..325a1567b99 100644 --- a/src/core.c/Variable.pm6 +++ b/src/core.c/Variable.pm6 @@ -20,10 +20,16 @@ my class Variable { $*W.throw( self.slash, |c ); } - submethod willdo(&block, $caller-levels = 3) { - $caller-levels == 3 - ?? -> { block(nqp::atkey(nqp::ctxcaller(nqp::ctxcaller(nqp::ctxcaller(nqp::ctx()))), self.name)) } - !! -> { block(nqp::atkey(nqp::ctxcaller(nqp::ctx()), self.name)) } + submethod willdo(&block) { + my str $name = self.name; + -> { + my $ctx := nqp::ctxcaller(nqp::ctx); + nqp::until( + nqp::existskey($ctx,$name), + $ctx := nqp::ctxcaller($ctx) + ); + block(nqp::atkey($ctx,$name)) + } } submethod native(Mu $what) { @@ -161,7 +167,7 @@ multi sub trait_mod:(Variable:D $v, $block, :end($)! ) { $*W.add_phaser($v.slash, 'END', $block); } multi sub trait_mod:(Variable:D $v, $block, :enter($)! ) { - $v.block.add_phaser('ENTER', $v.willdo($block, 1) ); + $v.block.add_phaser('ENTER', $v.willdo($block) ); $v.implicit-lexical-usage = True; } multi sub trait_mod:(Variable:D $v, $block, :leave($)! ) { @@ -177,7 +183,7 @@ multi sub trait_mod:(Variable:D $v, $block, :undo($)! ) { $v.implicit-lexical-usage = True; } multi sub trait_mod:(Variable:D $v, $block, :first($)! ) { - $v.block.add_phaser('FIRST', $v.willdo($block, 1)); + $v.block.add_phaser('FIRST', $v.willdo($block)); $v.implicit-lexical-usage = True; } multi sub trait_mod:(Variable:D $v, $block, :next($)! ) { @@ -187,7 +193,7 @@ multi sub trait_mod:(Variable:D $v, $block, :last($)! ) { $v.block.add_phaser('LAST', $block); } multi sub trait_mod:(Variable:D $v, $block, :pre($)! ) { - $v.block.add_phaser('PRE', $v.willdo($block, 1)); + $v.block.add_phaser('PRE', $v.willdo($block)); $v.implicit-lexical-usage = True; } multi sub trait_mod:(Variable:D $v, $block, :post($)! ) { diff --git a/t/02-rakudo/03-corekeys-6c.t b/t/02-rakudo/03-corekeys-6c.t index 140737d8d43..c9731ad49b3 100644 --- a/t/02-rakudo/03-corekeys-6c.t +++ b/t/02-rakudo/03-corekeys-6c.t @@ -611,7 +611,6 @@ my @expected = ( Q{ParallelSequence}, Q{Parameter}, Q{Perl}, - Q{PhasersList}, Q{Planned}, Q{Pod}, Q{Positional}, diff --git a/t/02-rakudo/03-corekeys-6d.t b/t/02-rakudo/03-corekeys-6d.t index a0b2c1d0f99..419d830769e 100644 --- a/t/02-rakudo/03-corekeys-6d.t +++ b/t/02-rakudo/03-corekeys-6d.t @@ -611,7 +611,6 @@ my @expected = ( Q{ParallelSequence}, Q{Parameter}, Q{Perl}, - Q{PhasersList}, Q{Planned}, Q{Pod}, Q{Positional}, diff --git a/t/02-rakudo/03-corekeys-6e.t b/t/02-rakudo/03-corekeys-6e.t index 774f26c3a09..7d28b9561cd 100644 --- a/t/02-rakudo/03-corekeys-6e.t +++ b/t/02-rakudo/03-corekeys-6e.t @@ -614,7 +614,6 @@ my @expected = ( Q{ParallelSequence}, Q{Parameter}, Q{Perl}, - Q{PhasersList}, Q{Planned}, Q{Pod}, Q{Positional}, diff --git a/t/02-rakudo/03-corekeys.t b/t/02-rakudo/03-corekeys.t index 4525dd3b256..d1024c5c31c 100644 --- a/t/02-rakudo/03-corekeys.t +++ b/t/02-rakudo/03-corekeys.t @@ -614,7 +614,6 @@ my @allowed = Q{ParallelSequence}, Q{Parameter}, Q{Perl}, - Q{PhasersList}, Q{Planned}, Q{Pod}, Q{Positional}, diff --git a/t/02-rakudo/04-settingkeys-6c.t b/t/02-rakudo/04-settingkeys-6c.t index 7b46d783ef6..04a2e5bc292 100644 --- a/t/02-rakudo/04-settingkeys-6c.t +++ b/t/02-rakudo/04-settingkeys-6c.t @@ -611,7 +611,6 @@ my %allowed = ( Q{ParallelSequence}, Q{Parameter}, Q{Perl}, - Q{PhasersList}, Q{Planned}, Q{Pod}, Q{Positional}, diff --git a/t/02-rakudo/04-settingkeys-6e.t b/t/02-rakudo/04-settingkeys-6e.t index 14e83c39045..8b22a435862 100644 --- a/t/02-rakudo/04-settingkeys-6e.t +++ b/t/02-rakudo/04-settingkeys-6e.t @@ -612,7 +612,6 @@ my %allowed = ( Q{ParallelSequence}, Q{Parameter}, Q{Perl}, - Q{PhasersList}, Q{Planned}, Q{Pod}, Q{Positional},