STATIC void su_yield(pTHX_ void *ud_) {
dMY_CXT;
PERL_CONTEXT *cx;
+ const char *which = ud_;
I32 cxix = MY_CXT.yield_storage.cxix;
I32 items = MY_CXT.yield_storage.items;
opcode type = OP_NULL;
break;
#endif
case CXt_SUBST:
- croak("yield() cannot target a substitution context");
+ croak("%s() cannot target a substitution context", which);
break;
default:
- croak("yield() don't know how to leave a %s context", SU_CXNAME(cxstack + cxix));
+ croak("%s() don't know how to leave a %s context",
+ which, SU_CXNAME(cxstack + cxix));
break;
}
croak("Can't return outside a subroutine");
}
+STATIC const char su_yield_name[] = "yield";
+
XS(XS_Scope__Upper_yield); /* prototype to pass -Wmissing-prototypes */
XS(XS_Scope__Upper_yield) {
/* See XS_Scope__Upper_unwind */
if (GIMME_V == G_SCALAR)
PL_stack_sp = PL_stack_base + PL_markstack_ptr[1] + 1;
- SAVEDESTRUCTOR_X(su_yield, NULL);
+ SAVEDESTRUCTOR_X(su_yield, su_yield_name);
+ return;
+}
+
+STATIC const char su_leave_name[] = "leave";
+
+XS(XS_Scope__Upper_leave); /* prototype to pass -Wmissing-prototypes */
+
+XS(XS_Scope__Upper_leave) {
+#ifdef dVAR
+ dVAR; dXSARGS;
+#else
+ dXSARGS;
+#endif
+ dMY_CXT;
+ I32 cxix;
+
+ PERL_UNUSED_VAR(cv); /* -W */
+ PERL_UNUSED_VAR(ax); /* -Wall */
+
+ MY_CXT.yield_storage.cxix = su_context_here();
+ MY_CXT.yield_storage.items = items;
+ MY_CXT.yield_storage.savesp = PL_stack_sp;
+ /* See XS_Scope__Upper_unwind */
+ if (GIMME_V == G_SCALAR)
+ PL_stack_sp = PL_stack_base + PL_markstack_ptr[1] + 1;
+ SAVEDESTRUCTOR_X(su_yield, su_leave_name);
return;
}
newXSproto("Scope::Upper::unwind", XS_Scope__Upper_unwind, file, NULL);
newXSproto("Scope::Upper::yield", XS_Scope__Upper_yield, file, NULL);
+ newXSproto("Scope::Upper::leave", XS_Scope__Upper_leave, file, NULL);
su_setup();
}
=item *
-return values immediately to an upper level with L</unwind> and L</yield>, and know which context was in use then with L</want_at> ;
+return values immediately to an upper level with L</unwind>, L</yield> and L</leave>, and know which context was in use then with L</want_at> ;
=item *
Like for L</unwind>, the upper context isn't coerced onto C<@values>.
+=head2 C<leave>
+
+ leave;
+ leave @values;
+
+Immediately returns C<@values> from the current block, whatever it may be (besides a C<s///e> substitution context).
+C<leave> is actually a synonym for C<unwind HERE>, while C<leave @values> is a synonym for C<yield @values, HERE>.
+
=head2 C<want_at>
my $want = want_at;
=head1 EXPORT
-The functions L</reap>, L</localize>, L</localize_elem>, L</localize_delete>, L</unwind>, L</yield>, L</want_at> and L</uplevel> are only exported on request, either individually or by the tags C<':funcs'> and C<':all'>.
+The functions L</reap>, L</localize>, L</localize_elem>, L</localize_delete>, L</unwind>, L</yield>, L</leave>, L</want_at> and L</uplevel> are only exported on request, either individually or by the tags C<':funcs'> and C<':all'>.
The constant L</SU_THREADSAFE> is also only exported on request, individually or by the tags C<':consts'> and C<':all'>.
funcs => [ qw<
reap
localize localize_elem localize_delete
- unwind yield
+ unwind yield leave
want_at
uplevel
uid validate_uid
use strict;
use warnings;
-use Test::More tests => 2 * 18;
+use Test::More tests => 2 * 19;
require Scope::Upper;
localize_delete => '$$;$',
unwind => undef,
yield => undef,
+ leave => undef,
want_at => ';$',
uplevel => '&@',
uid => ';$',
use Test::More tests => 18;
-use Scope::Upper qw<yield>;
+use Scope::Upper qw<yield leave>;
my @res;
use strict;
use warnings;
-use Test::More tests => 4 * 3;
+use Test::More tests => 4 * 3 + 3;
use lib 't/lib';
use VPIT::TestHelpers;
-use Scope::Upper qw<yield HERE>;
+use Scope::Upper qw<yield leave HERE>;
# Test timely destruction of values returned from yield()
}
is $destroyed, 1, "$desc: destroyed 2";
}
+
+# Test leave
+
+{
+ my @res = (1, do {
+ leave;
+ 'XXX';
+ }, 2);
+ is "@res", '1 2', 'leave without arguments';
+}
+
+{
+ my @res = (1, do {
+ leave 2, 3;
+ 'XXX';
+ }, 4);
+ is "@res", '1 2 3 4', 'leave with arguments';
+}
+
+{
+ my $s = 'a';
+ local $@;
+ eval {
+ $s =~ s/./leave; die 'not reached'/e;
+ };
+ my $err = $@;
+ my $line = __LINE__-3;
+ like $err,
+ qr/^leave\(\) cannot target a substitution context at \Q$0\E line $line/,
+ 'leave() cannot exit subst';
+}