samples/tag.pl
t/00-load.t
t/01-import.t
-t/05-TOPLEVEL.t
+t/05-words.t
t/10-reap.t
t/11-reap-level.t
t/12-reap-block.t
#include "perl.h"
#include "XSUB.h"
+#define __PACKAGE__ "Scope::Upper"
+
#ifndef SU_DEBUG
# define SU_DEBUG 0
#endif
if (level > cxstack_ix) \
level = cxstack_ix;
+#define SU_DOPOPTOCX(t) \
+ STMT_START { \
+ I32 i, cxix = cxstack_ix, from = 0; \
+ if (items) \
+ from = SvIV(ST(0)); \
+ for (i = cxix - from; i >= 0; --i) { \
+ if (CxTYPE(&cxstack[i]) == t) { \
+ ST(0) = sv_2mortal(newSViv(cxix - i)); \
+ XSRETURN(1); \
+ } \
+ } \
+ XSRETURN_UNDEF; \
+ } STMT_END
+
/* --- XS ------------------------------------------------------------------ */
MODULE = Scope::Upper PACKAGE = Scope::Upper
PROTOTYPES: ENABLE
+BOOT:
+{
+ HV *stash = gv_stashpv(__PACKAGE__, 1);
+ newCONSTSUB(stash, "CURRENT", newSViv(0));
+}
+
SV *
-TOPLEVEL()
+TOP()
PROTOTYPE:
CODE:
RETVAL = newSViv(cxstack_ix);
OUTPUT:
RETVAL
+SV *
+UP(...)
+PROTOTYPE: ;$
+PREINIT:
+ I32 i = 0;
+ I32 cxix = cxstack_ix;
+CODE:
+ if (items)
+ i = SvIV(ST(0));
+ if (++i > cxix)
+ i = cxix;
+ RETVAL = newSViv(i);
+OUTPUT:
+ RETVAL
+
+SV *
+DOWN(...)
+PROTOTYPE: ;$
+PREINIT:
+ I32 i = 0;
+CODE:
+ if (items)
+ i = SvIV(ST(0));
+ if (--i < 0)
+ i = 0;
+ RETVAL = newSViv(i);
+OUTPUT:
+ RETVAL
+
+void
+SUB(...)
+PROTOTYPE: ;$
+PPCODE:
+ SU_DOPOPTOCX(CXt_SUB);
+
+void
+EVAL(...)
+PROTOTYPE: ;$
+PPCODE:
+ SU_DOPOPTOCX(CXt_EVAL);
+
void
reap(SV *hook, ...)
PROTOTYPE: &;$
=back
-=head2 C<TOPLEVEL>
+=head1 WORDS
+
+=head2 C<TOP>
Returns the level that currently represents the highest scope.
+=head2 C<CURRENT>
+
+The current level - i.e. C<0>.
+
+=head2 C<UP $from>
+
+The level of the scope just above C<$from>.
+
+=head2 C<DOWN $from>
+
+The level of the scope just below C<$from>.
+
+=head2 C<SUB $from>
+
+The level of the closest subroutine context above C<$from>.
+
+=head2 C<EVAL $from>
+
+The level of the closest eval context above C<$from>.
+
+If C<$from> is omitted in any of those functions, the current level is used as the reference level.
+
=head1 EXPORT
-The functions L</reap>, L</localize>, L</localize_elem>, L</localize_delete> and L</TOPLEVEL> 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> and L</localize_delete> are only exported on request, either individually or by the tags C<':funcs'> and C<':all'>.
+
+Same goes for the words L</TOP>, L</CURRENT>, L</UP>, L</DOWN>, L</SUB> and L</EVAL> that are only exported on request, individually or by the tags C<':words'> and C<':all'>.
=cut
our @EXPORT = ();
our %EXPORT_TAGS = (
- funcs => [ qw/reap localize localize_elem localize_delete TOPLEVEL/ ],
+ funcs => [ qw/reap localize localize_elem localize_delete/ ],
+ words => [ qw/TOP CURRENT UP DOWN SUB EVAL/ ],
);
our @EXPORT_OK = map { @$_ } values %EXPORT_TAGS;
$EXPORT_TAGS{'all'} = [ @EXPORT_OK ];
use strict;
use warnings;
-use Test::More tests => 4;
+use Test::More tests => 9;
require Scope::Upper;
-for (qw/reap localize localize_elem TOPLEVEL/) {
+for (qw/reap localize localize_elem TOP CURRENT UP DOWN SUB EVAL/) {
eval { Scope::Upper->import($_) };
is($@, '', 'import ' . $_);
}
+++ /dev/null
-#!perl -T
-
-use strict;
-use warnings;
-
-use Test::More tests => 9;
-
-use Scope::Upper qw/TOPLEVEL/;
-
-is TOPLEVEL, 0, 'main is 0';
-
-{
- is TOPLEVEL, 1, '{ 1 }';
-}
-
-do {
- is TOPLEVEL, 1, 'do { 1 }';
-};
-
-eval {
- is TOPLEVEL, 1, 'eval { 1 }';
-};
-
-eval q[
- is TOPLEVEL, 1, 'eval "1"';
-];
-
-do {
- is TOPLEVEL, 1, 'do { 1 } while (0)';
-} while (0);
-
-sub {
- is TOPLEVEL, 1, 'sub { 1 }';
-}->();
-
-for (1) {
- is TOPLEVEL, 1, 'for () { 1 }';
-}
-
-do {
- eval {
- do {
- sub {
- eval q[
- {
- is TOPLEVEL, 6, 'all'
- }
- ];
- }->();
- }
- };
-} while (0);
--- /dev/null
+#!perl -T
+
+use strict;
+use warnings;
+
+use Test::More tests => 33;
+
+use Scope::Upper qw/:words/;
+
+is CURRENT, 0, 'main : current';
+is TOP, 0, 'main : top';
+is UP, 0, 'main : up';
+is DOWN, 0, 'main : down';
+is SUB, undef, 'main : sub';
+is EVAL, undef, 'main : eval';
+
+{
+ is CURRENT, 0, '{ 1 } : current';
+ is TOP, 1, '{ 1 } : top';
+ is UP, 1, '{ 1 } : up';
+ is DOWN, 0, '{ 1 } : down';
+ is DOWN(UP), 0, '{ 1 } : up then down';
+ is UP(DOWN), 1, '{ 1 } : down then up';
+}
+
+do {
+ is TOP, 1, 'do { 1 } : top';
+ is SUB, undef, 'do { 1 } : sub';
+ is EVAL, undef, 'do { 1 } : eval';
+};
+
+eval {
+ is TOP, 1, 'eval { 1 } : top';
+ is SUB, undef, 'eval { 1 } : sub';
+ is EVAL, 0, 'eval { 1 } : eval';
+};
+
+eval q[
+ is TOP, 1, 'eval "1" : top';
+ is SUB, undef, 'eval "1" : sub';
+ is EVAL, 0, 'eval "1" : eval';
+];
+
+do {
+ is TOP, 1, 'do { 1 } while (0) : top';
+} while (0);
+
+sub {
+ is TOP, 1, 'sub { 1 } : top';
+ is SUB, 0, 'sub { 1 } : sub';
+ is EVAL, undef, 'sub { 1 } : eval';
+}->();
+
+for (1) {
+ is TOP, 1, 'for () { 1 } : top';
+}
+
+do {
+ eval {
+ do {
+ sub {
+ eval q[
+ {
+ is CURRENT, 0, 'mixed : current';
+ is TOP, 6, 'mixed : top';
+ is SUB, 2, 'mixed : first sub';
+ is SUB(SUB), 2, 'mixed : still first sub';
+ is EVAL, 1, 'mixed : first eval';
+ is EVAL(EVAL), 1, 'mixed : still first eval';
+ is EVAL(UP(EVAL)), 4, 'mixed : second eval';
+ }
+ ];
+ }->();
+ }
+ };
+} while (0);