X-Git-Url: http://git.vpit.fr/?a=blobdiff_plain;f=lib%2FScope%2FUpper.pm;h=377e434e5bd8adc8c6e2be7e2bd4f20b692155c8;hb=cb2377339e35cfbadcaf35f75434972bbaf741bc;hp=6bce12aaf43e6b86e94ff8847a9fc7f41a021a1f;hpb=f1d34eeb61ec676615d9aafe69110af9b0dc302a;p=perl%2Fmodules%2FScope-Upper.git diff --git a/lib/Scope/Upper.pm b/lib/Scope/Upper.pm index 6bce12a..377e434 100644 --- a/lib/Scope/Upper.pm +++ b/lib/Scope/Upper.pm @@ -9,13 +9,13 @@ Scope::Upper - Act on upper scopes. =head1 VERSION -Version 0.14 +Version 0.17 =cut our $VERSION; BEGIN { - $VERSION = '0.14'; + $VERSION = '0.17'; } =head1 SYNOPSIS @@ -24,7 +24,7 @@ L, L, L, L and L : package Scope; - use Scope::Upper qw/reap localize localize_elem localize_delete :words/; + use Scope::Upper qw; sub new { my ($class, $name) = @_; @@ -87,7 +87,7 @@ L and L : package Try; - use Scope::Upper qw/unwind want_at :words/; + use Scope::Upper qw; sub try (&) { my @result = shift->(); @@ -99,16 +99,35 @@ L and L : sub zap { try { - my @things = qw/a b c/; + my @things = qw; return @things; # returns to try() and then outside zap() # not reached }; # not reached } - my @stuff = zap(); # @stuff contains qw/a b c/ + my @stuff = zap(); # @stuff contains qw my $stuff = zap(); # $stuff contains 3 +L : + + package Uplevel; + + use Scope::Upper qw; + + sub target { + faker(@_); + } + + sub faker { + uplevel { + my $sub = (caller 0)[3]; + print "$_[0] from $sub()"; + } @_ => CALLER(1); + } + + target('hello'); # "hello from Uplevel::target()" + =head1 DESCRIPTION This module lets you defer actions I that will take place when the control flow returns into an upper scope. @@ -126,7 +145,11 @@ localize variables, array/hash values or deletions of elements in higher context =item * -return values immediately to an upper level with L, and know which context was in use then with L. +return values immediately to an upper level with L, and know which context was in use then with L ; + +=item * + +execute a subroutine in the setting of an upper subroutine stack frame with L. =back @@ -261,6 +284,75 @@ The previous example can then be "corrected" : will rightfully set C<$num> to C<26>. +=head2 C + +Executes the code reference C<$code> with arguments C<@args> as if it were located at the subroutine stack frame pointed by C<$context>, effectively fooling C and C into believing that the call actually happened higher in the stack. +The code is executed in the context of the C call, and what it returns is returned as-is by C. + + sub target { + faker(@_); + } + + sub faker { + uplevel { + map { 1 / $_ } @_; + } @_ => CALLER(1); + } + + my @inverses = target(1, 2, 4); # @inverses contains (0, 0.5, 0.25) + my $count = target(1, 2, 4); # $count is 3 + +L also implements a pure-Perl version of C. +Both are identical, with the following caveats : + +=over 4 + +=item * + +The L implementation of C may execute a code reference in the context of B upper stack frame. +The L version can only uplevel to a B stack frame, and will croak if you try to target an C or a format. + +=item * + +Exceptions thrown from the code called by this version of C will not be caught by C blocks between the target frame and the uplevel call, while they will for L's version. +This means that : + + eval { + sub { + local $@; + eval { + sub { + uplevel { die 'wut' } CALLER(2); # for Scope::Upper + # uplevel(3, sub { die 'wut' }) # for Sub::Uplevel + }->(); + }; + print "inner block: $@"; + $@ and exit; + }->(); + }; + print "outer block: $@"; + +will print "inner block: wut..." with L and "outer block: wut..." with L. + +=item * + +L globally overrides the Perl keyword C, while L does not. + +=back + +A simple wrapper lets you mimic the interface of L : + + use Scope::Upper; + + sub uplevel { + my $frame = shift; + my $code = shift; + my $cxt = Scope::Upper::CALLER($frame); + &Scope::Upper::uplevel($code => @_ => $cxt); + } + +Albeit the three exceptions listed above, it passes all the tests of L. + =head1 CONSTANTS =head2 C @@ -353,26 +445,29 @@ Where L, L and L act depending on t # $cxt = SCOPE(4), UP SUB UP SUB, or UP SUB EVAL, or UP CALLER(2), or TOP ... -Where L and L point to depending on the C<$cxt>: +Where L, L and L point to depending on the C<$cxt>: sub { eval { sub { { - unwind @things => $cxt; + unwind @things => $cxt; # or uplevel { ... } $cxt; ... } ... }->(); # $cxt = SCOPE(0 .. 1), or HERE, or UP, or SUB, or CALLER(0) ... - }; # $cxt = SCOPE(2), or UP UP, or UP SUB, or EVAL, or CALLER(1) + }; # $cxt = SCOPE(2), or UP UP, or UP SUB, or EVAL, or CALLER(1) (*) ... }->(); # $cxt = SCOPE(3), or SUB UP SUB, or SUB EVAL, or CALLER(2) ... + # (*) Note that uplevel() will croak if you pass that scope frame, + # because it cannot target eval scopes. + =head1 EXPORT -The functions L, L, L, L, L and L are only exported on request, either individually or by the tags C<':funcs'> and C<':all'>. +The functions L, L, L, L, L, L and L are only exported on request, either individually or by the tags C<':funcs'> and C<':all'>. The constant L is also only exported on request, individually or by the tags C<':consts'> and C<':all'>. @@ -380,13 +475,18 @@ Same goes for the words L, L, L, L, L, L =cut -use base qw/Exporter/; +use base qw; our @EXPORT = (); our %EXPORT_TAGS = ( - funcs => [ qw/reap localize localize_elem localize_delete unwind want_at/ ], - words => [ qw/TOP HERE UP SUB EVAL SCOPE CALLER/ ], - consts => [ qw/SU_THREADSAFE/ ], + funcs => [ qw< + reap + localize localize_elem localize_delete + unwind want_at + uplevel + > ], + words => [ qw ], + consts => [ qw ], ); our @EXPORT_OK = map { @$_ } values %EXPORT_TAGS; $EXPORT_TAGS{'all'} = [ @EXPORT_OK ]; @@ -420,6 +520,12 @@ However, it's possible to hook the end of the current scope compilation with L to replace an L'd code frame does not work when a custom runloop is used or when debugging flags are set with C. +In those two cases, L will look for a C statement in its callback and, if there is one, throw an exception before executing the code. + +Moreover, in order to handle C statements properly, L currently has to suffer a run-time overhead proportional to the size of the the callback in every case (with a small ratio), and proportional to the size of B the code executed as the result of the L call (including subroutine calls inside the callback) when a C statement is found in the L callback. +Despite this shortcoming, this XS version of L should still run way faster than the pure-Perl version from L. + =head1 DEPENDENCIES L (standard since perl 5.006). @@ -430,6 +536,8 @@ L, L. L, L, L, L. +L. + L is a thin wrapper around L that gives you a continuation passing style interface to L. It's easier to use, but it requires you to have control over the scope where you want to return.