X-Git-Url: http://git.vpit.fr/?a=blobdiff_plain;f=lib%2FScope%2FUpper.pm;h=377e434e5bd8adc8c6e2be7e2bd4f20b692155c8;hb=refs%2Ftags%2Fv0.17;hp=f6849e5be6f64f2dff247e0ffe6fd82483cc7924;hpb=fe6605581cbe68b5935e72c98a8a685379d1f320;p=perl%2Fmodules%2FScope-Upper.git
diff --git a/lib/Scope/Upper.pm b/lib/Scope/Upper.pm
index f6849e5..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.12
+Version 0.17
=cut
our $VERSION;
BEGIN {
- $VERSION = '0.12';
+ $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.
@@ -462,7 +570,7 @@ Thanks to Shawn M. Moore for motivation.
=head1 COPYRIGHT & LICENSE
-Copyright 2008,2009,2010 Vincent Pit, all rights reserved.
+Copyright 2008,2009,2010,2011 Vincent Pit, all rights reserved.
This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.