]> git.vpit.fr Git - perl/modules/re-engine-Plugin.git/commitdiff
Importing re-engine-Plugin-0.04_01.tar.gz v0.04_01
authorÆvar Arnfjörð Bjarmason <avar@cpan.org>
Wed, 30 May 2007 01:52:00 +0000 (01:52 +0000)
committerVincent Pit <vince@profvince.com>
Wed, 30 May 2007 01:52:00 +0000 (01:52 +0000)
22 files changed:
ChangeLog
MANIFEST
Plugin.h
Plugin.pm
Plugin.pod
Plugin.xs
named-capture.patch [new file with mode: 0644]
t/methods/stash.t
t/methods/str/modify.t [new file with mode: 0644]
t/methods/str/types.t
t/named_buff/CLEAR.t [new file with mode: 0644]
t/named_buff/DELETE.t [new file with mode: 0644]
t/named_buff/EXISTS.t [new file with mode: 0644]
t/named_buff/FETCH.t [new file with mode: 0644]
t/named_buff/FIRSTKEY.t [new file with mode: 0644]
t/named_buff/NEXTKEY.t [new file with mode: 0644]
t/named_buff/SCALAR.t [new file with mode: 0644]
t/named_buff/STORE.t [new file with mode: 0644]
t/usage/basic.pm [new file with mode: 0644]
t/usage/basic.t [new file with mode: 0644]
t/usage/import.pm [new file with mode: 0644]
t/usage/import.t [new file with mode: 0644]

index 50a3310c82cf00ebdc29d974ebb1abbc8c4da8de..13757f922bff2889af9aebfedf9bf56d0d5166f0 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,22 @@
+2007-05-29 Ævar Arnfjörð Bjarmason <avar@cpan.org> (0.04_01)
+
+       * Plugin.xs: Implementation of named buffer callbacks, there is
+                    some debate about whether this patch should go into
+                    blead so I'm making a dev release
+       * named_capture.patch: The patch to blead that makes this tick
+       * Plugin.xs: Dumped CODE: in favor of PPCODE:, the former is teh sux
+       * Plugin.xs: Minor cleanups here and there
+       * Plugin.pod: New todo and a developer release notice
+       * Plugin.h: Named buffer callbacks
+       * Plugin.pm: Named buffer callbacks
+       * t/methods/stash.t: Test return value of $rx->stash({});
+       * t/methods/str/modify.t: New test for modification of ->str when
+                                 a reference is used
+       * t/methods/str/types.t: This was testing pattern types, fixed
+       
+       * t/Example.[t|pm]: Moved to t/usage
+       * t/named_buff: Tests for named buffer callbacks
+
 2007-05-17 Ævar Arnfjörð Bjarmason <avar@cpan.org> (0.03)
        
        * Plugin.xs: The exec callback would call_sv on a NULL value
index 97d9d7e3709151f1a38462fd85c20dfb4621d128..eb36f03f6f658c2463fd9ff38da82da9ac6f6854 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -14,6 +14,7 @@ inc/Module/Install/WriteAll.pm
 Makefile.PL
 MANIFEST                       This list of files
 META.yml
+named-capture.patch
 Plugin.h
 Plugin.pm
 Plugin.pod
@@ -21,8 +22,6 @@ Plugin.xs
 t/00-compile.t
 t/eval-comp.t
 t/eval-exec.t
-t/Example.pm
-t/Example.t
 t/import.t
 t/methods.t
 t/methods/free.t
@@ -33,11 +32,24 @@ t/methods/mod.t
 t/methods/pattern/modify.t
 t/methods/pattern/types.t
 t/methods/stash.t
+t/methods/str/modify.t
 t/methods/str/types.t
 t/methods/str/undef.t
+t/named_buff/CLEAR.t
+t/named_buff/DELETE.t
+t/named_buff/EXISTS.t
+t/named_buff/FETCH.t
+t/named_buff/FIRSTKEY.t
+t/named_buff/NEXTKEY.t
+t/named_buff/SCALAR.t
+t/named_buff/STORE.t
 t/num_buff/FETCH.t
 t/num_buff/LENGTH.t
 t/num_buff/STORE.t
 t/taint/rx.t
 t/taint/util.t
+t/usage/basic.pm
+t/usage/basic.t
+t/usage/import.pm
+t/usage/import.t
 typemap
index 2124ad871a51822b04498f25e567b64ccbd1e47d..668aec9d95caa41ceb7d4edb71d188866ae1d5d8 100644 (file)
--- a/Plugin.h
+++ b/Plugin.h
@@ -30,6 +30,20 @@ EXTERN_C I32      Plugin_numbered_buff_LENGTH(pTHX_ REGEXP * const,
                                               const SV * const, const I32);
 EXTERN_C SV *     Plugin_named_buff_FETCH(pTHX_ REGEXP * const, SV * const,
                                           const U32);
+EXTERN_C void     Plugin_named_buff_STORE(pTHX_ REGEXP * const rx,
+                                          SV * const key, SV * const value,
+                                          const U32 flags);
+EXTERN_C void     Plugin_named_buff_DELETE(pTHX_ REGEXP * const rx,
+                                           SV * const key, const U32 flags);
+EXTERN_C void     Plugin_named_buff_CLEAR (pTHX_ REGEXP * const rx, const U32 flags);
+EXTERN_C bool     Plugin_named_buff_EXISTS (pTHX_ REGEXP * const rx,
+                                            SV * const key, const U32 flags);
+EXTERN_C SV *     Plugin_named_buff_FIRSTKEY (pTHX_ REGEXP * const rx,
+                                              const U32 flags);
+EXTERN_C SV *     Plugin_named_buff_NEXTKEY (pTHX_ REGEXP * const rx,
+                                             SV * const lastkey, const U32 flags);
+EXTERN_C SV *     Plugin_named_buff_SCALAR (pTHX_ REGEXP * const rx,
+                                            const U32 flags);
 EXTERN_C SV *     Plugin_package(pTHX_ REGEXP * const);
 #ifdef USE_ITHREADS
 EXTERN_C void *   Plugin_dupe(pTHX_ REGEXP * const, CLONE_PARAMS *);
@@ -51,6 +65,13 @@ const regexp_engine engine_plugin = {
     Plugin_numbered_buff_STORE,
     Plugin_numbered_buff_LENGTH,
     Plugin_named_buff_FETCH,
+    Plugin_named_buff_STORE,
+    Plugin_named_buff_DELETE,
+    Plugin_named_buff_CLEAR,
+    Plugin_named_buff_EXISTS,
+    Plugin_named_buff_FIRSTKEY,
+    Plugin_named_buff_NEXTKEY,
+    Plugin_named_buff_SCALAR,
     Plugin_package,
 #if defined(USE_ITHREADS)        
     Plugin_dupe,
@@ -75,10 +96,18 @@ typedef struct replug {
      * Callbacks
      */
 
-    SV * cb_free;
-
     /* ->num_captures */
     SV * cb_num_capture_buff_FETCH;
     SV * cb_num_capture_buff_STORE;
     SV * cb_num_capture_buff_LENGTH;
+
+    /* ->named_captures */
+    SV * cb_named_capture_buff_FETCH;
+    SV * cb_named_capture_buff_STORE;
+    SV * cb_named_capture_buff_DELETE;
+    SV * cb_named_capture_buff_CLEAR;
+    SV * cb_named_capture_buff_EXISTS;
+    SV * cb_named_capture_buff_FIRSTKEY;
+    SV * cb_named_capture_buff_NEXTKEY;
+    SV * cb_named_capture_buff_SCALAR;
 } *re__engine__Plugin;
index 220dfdfd9b94ebb9fc6340e2b95d15563d98171d..80fa3892b81e68fcbaf1013a024cc009ee4473c0 100644 (file)
--- a/Plugin.pm
+++ b/Plugin.pm
@@ -5,7 +5,7 @@ use base 'Regexp';
 use strict;\r
 use XSLoader ();\r
 \r
-our $VERSION = '0.03';\r
+our $VERSION = '0.04_01';\r
 \r
 XSLoader::load __PACKAGE__, $VERSION;\r
 \r
@@ -26,7 +26,7 @@ sub import
     my ($pkg, %sub) = @_;\r
 \r
     # Valid callbacks\r
-    my @callback = qw(comp exec intuit checkstr free dupe);\r
+    my @callback = qw(comp exec); #intuit checkstr free dupe);\r
 \r
     for (@callback) {\r
         next unless exists $sub{$_};\r
@@ -83,4 +83,15 @@ sub num_captures
     }\r
 }\r
 \r
+sub named_captures\r
+{\r
+    my ($re, %callback) = @_;\r
+\r
+    for my $key (keys %callback) {\r
+        $key =~ y/a-z/A-Z/; # ASCII uc\r
+        my $name = '_named_capture_buff_' . $key;\r
+        $re->$name( $callback{$key} );\r
+    }\r
+}\r
+\r
 1;\r
index 3ae9882d8bcd5ff26cbd5663f3ccfae1e9e916ab..1438f64dde0b50c647ac5e7e54f085b0148a19e5 100644 (file)
@@ -2,6 +2,11 @@
 
 re::engine::Plugin - API to write custom regex engines
 
+=head1 NOTICE
+
+This is a B<developer release> that requires a patch to blead to work,
+the patch can be found in F<named_capture.patch> in this distribution.
+
 =head1 DESCRIPTION
 
 As of perl 5.9.5 it's possible to lexically replace perl's built-in
@@ -224,12 +229,10 @@ name an example).
 
 =head2 named_captures
 
-B<TODO>: implement
+B<TODO>: document
 
-perl internals still needs to be changed to support this but when it's
-done it'll allow the binding of C<%+> and C<%-> and support the
-L<Tie::Hash> methods FETCH, STORE, DELETE, CLEAR, EXISTS, FIRSTKEY,
-NEXTKEY and SCALAR.
+This is implemented but not documented, see F<t/named_buff> for usage
+examples.
 
 =head1 Tainting
 
@@ -300,6 +303,11 @@ I<here be dragons>
 
 =item *
 
+Export constants defined as macros in core relevant to our interests,
+e.g. PMf_ stuff and things needed by extflags.
+
+=item *
+
 Engines implemented with this module don't support C<s///> and C<split
 //>, the appropriate parts of the C<REGEXP> struct need to be wrapped
 and documented.
index aa95e8b468b67df5274baa60db3107ddb4d3d644..6d003d9c53272b2a9358d8633d31e052dc38796d 100644 (file)
--- a/Plugin.xs
+++ b/Plugin.xs
@@ -67,7 +67,7 @@ Plugin_comp(pTHX_ const SV * const pattern, const U32 flags)
     rx->extflags = flags;          /* Flags for perl to use */
     rx->engine = RE_ENGINE_PLUGIN; /* Compile to use this engine */
 
-    /* Store a precompiled regexp for pp_regcomp to use */
+    /* Precompiled regexp for pp_regcomp to use */
     rx->prelen = plen;
     rx->precomp = savepvn(exp, rx->prelen);
 
@@ -216,7 +216,6 @@ Plugin_dupe(pTHX_ const REGEXP * rx, CLONE_PARAMS *param)
     return rx->pprivate;
 }
 
-
 void
 Plugin_numbered_buff_FETCH(pTHX_ REGEXP * const rx, const I32 paren,
                            SV * const sv)
@@ -292,9 +291,7 @@ Plugin_numbered_buff_LENGTH(pTHX_ REGEXP * const rx, const SV * const sv,
     dSP;
     I32 items;
     SV * callback;
-    re__engine__Plugin self;
-
-    SELF_FROM_PPRIVATE(self,rx->pprivate);
+    GET_SELF_FROM_PPRIVATE(rx->pprivate);
 
     callback = self->cb_num_capture_buff_LENGTH;
 
@@ -328,11 +325,262 @@ Plugin_numbered_buff_LENGTH(pTHX_ REGEXP * const rx, const SV * const sv,
 SV*
 Plugin_named_buff_FETCH(pTHX_ REGEXP * const rx, SV * const key, U32 flags)
 {
-    PERL_UNUSED_ARG(rx);
-    PERL_UNUSED_ARG(key);
-    PERL_UNUSED_ARG(flags);
+    dSP;
+    SV * callback;
+    GET_SELF_FROM_PPRIVATE(rx->pprivate);
 
-    return NULL;
+    callback = self->cb_named_capture_buff_FETCH;
+
+    if (callback) {
+        ENTER;
+        SAVETMPS;
+   
+        PUSHMARK(SP);
+        XPUSHs(rx->pprivate);
+        XPUSHs(SvREFCNT_inc(key));
+        XPUSHs(sv_2mortal(newSViv(flags & 1 ? 1 : 0)));
+        PUTBACK;
+
+        call_sv(callback, G_SCALAR);
+
+        SPAGAIN;
+
+        SV* ret = POPs;
+        SvREFCNT_inc(ret);
+
+        PUTBACK;
+        FREETMPS;
+        LEAVE;
+
+        return ret;
+    } else {
+        return NULL;
+    }
+}
+
+void
+Plugin_named_buff_STORE(pTHX_ REGEXP * const rx, SV * const key,
+                        SV * const value, const U32 flags)
+{
+    dSP;
+    SV * callback;
+    GET_SELF_FROM_PPRIVATE(rx->pprivate);
+
+    callback = self->cb_named_capture_buff_STORE;
+
+    if (callback) {
+        ENTER;
+        SAVETMPS;
+   
+        PUSHMARK(SP);
+        XPUSHs(rx->pprivate);
+        XPUSHs(SvREFCNT_inc(key));
+        XPUSHs(SvREFCNT_inc(value));
+        XPUSHs(sv_2mortal(newSViv(flags)));
+        PUTBACK;
+
+        call_sv(callback, G_DISCARD);
+
+        PUTBACK;
+        FREETMPS;
+        LEAVE;
+    }
+}
+
+void
+Plugin_named_buff_DELETE(pTHX_ REGEXP * const rx, SV * const key, const U32 flags)
+{
+    dSP;
+    SV * callback;
+    GET_SELF_FROM_PPRIVATE(rx->pprivate);
+
+    callback = self->cb_named_capture_buff_DELETE;
+
+    if (callback) {
+        ENTER;
+        SAVETMPS;
+   
+        PUSHMARK(SP);
+        XPUSHs(rx->pprivate);
+        XPUSHs(SvREFCNT_inc(key));
+        XPUSHs(sv_2mortal(newSViv(flags)));
+        PUTBACK;
+
+        call_sv(callback, G_DISCARD);
+
+        PUTBACK;
+        FREETMPS;
+        LEAVE;
+    }
+}
+
+void
+Plugin_named_buff_CLEAR(pTHX_ REGEXP * const rx, const U32 flags)
+{
+    dSP;
+    SV * callback;
+    GET_SELF_FROM_PPRIVATE(rx->pprivate);
+
+    callback = self->cb_named_capture_buff_CLEAR;
+
+    if (callback) {
+        ENTER;
+        SAVETMPS;
+   
+        PUSHMARK(SP);
+        XPUSHs(rx->pprivate);
+        XPUSHs(sv_2mortal(newSViv(flags)));
+        PUTBACK;
+
+        call_sv(callback, G_DISCARD);
+
+        PUTBACK;
+        FREETMPS;
+        LEAVE;
+    }
+}
+
+bool
+Plugin_named_buff_EXISTS(pTHX_ REGEXP * const rx, SV * const key,
+                         const U32 flags)
+{
+    dSP;
+    SV * callback;
+    bool truthiness = FALSE;
+    GET_SELF_FROM_PPRIVATE(rx->pprivate);
+
+    callback = self->cb_named_capture_buff_EXISTS;
+
+    if (callback) {
+        ENTER;
+        SAVETMPS;
+   
+        PUSHMARK(SP);
+        XPUSHs(rx->pprivate);
+        XPUSHs(SvREFCNT_inc(key));
+        XPUSHs(sv_2mortal(newSViv(flags)));
+        PUTBACK;
+
+        call_sv(callback, G_SCALAR);
+
+        SPAGAIN;
+
+        SV * ret = POPs;
+        truthiness = SvTRUE(ret);
+
+        PUTBACK;
+        FREETMPS;
+        LEAVE;
+    }
+
+    return truthiness;
+}
+
+SV*
+Plugin_named_buff_FIRSTKEY(pTHX_ REGEXP * const rx, const U32 flags)
+{
+    dSP;
+    SV * callback;
+    GET_SELF_FROM_PPRIVATE(rx->pprivate);
+
+    callback = self->cb_named_capture_buff_FIRSTKEY;
+
+    if (callback) {
+        ENTER;
+        SAVETMPS;
+   
+        PUSHMARK(SP);
+        XPUSHs(rx->pprivate);
+        XPUSHs(sv_2mortal(newSViv(flags)));
+        PUTBACK;
+
+        call_sv(callback, G_SCALAR);
+
+        SPAGAIN;
+
+        SV * ret = POPs;
+        SvREFCNT_inc(ret);
+
+        PUTBACK;
+        FREETMPS;
+        LEAVE;
+
+        return ret;
+    } else {
+        return NULL;
+    }
+}
+
+SV*
+Plugin_named_buff_NEXTKEY(pTHX_ REGEXP * const rx, SV * const lastkey,
+                          const U32 flags)
+{
+    dSP;
+    SV * callback;
+    GET_SELF_FROM_PPRIVATE(rx->pprivate);
+
+    callback = self->cb_named_capture_buff_NEXTKEY;
+
+    if (callback) {
+        ENTER;
+        SAVETMPS;
+   
+        PUSHMARK(SP);
+        XPUSHs(rx->pprivate);
+        XPUSHs(SvREFCNT_inc(lastkey));
+        XPUSHs(sv_2mortal(newSViv(flags)));
+        PUTBACK;
+
+        call_sv(callback, G_SCALAR);
+
+        SPAGAIN;
+
+        SV * ret = POPs;
+        SvREFCNT_inc(ret);
+
+        PUTBACK;
+        FREETMPS;
+        LEAVE;
+
+        return ret;
+    } else {
+        return NULL;
+    }
+}
+
+SV*
+Plugin_named_buff_SCALAR(pTHX_ REGEXP * const rx, const U32 flags)
+{
+    dSP;
+    SV * callback;
+    GET_SELF_FROM_PPRIVATE(rx->pprivate);
+
+    callback = self->cb_named_capture_buff_SCALAR;
+
+    if (callback) {
+        ENTER;
+        SAVETMPS;
+   
+        PUSHMARK(SP);
+        XPUSHs(rx->pprivate);
+        XPUSHs(sv_2mortal(newSViv(flags)));
+        PUTBACK;
+
+        call_sv(callback, G_SCALAR);
+
+        SPAGAIN;
+
+        SV * ret = POPs;
+        SvREFCNT_inc(ret);
+
+        PUTBACK;
+        FREETMPS;
+        LEAVE;
+
+        return ret;
+    } else {
+        return NULL;
+    }
 }
 
 SV*
@@ -343,25 +591,19 @@ Plugin_package(pTHX_ REGEXP * const rx)
 }
 
 MODULE = re::engine::Plugin    PACKAGE = re::engine::Plugin
-PROTOTYPES: ENABLE
+PROTOTYPES: DISABLE
 
-SV *
+void
 pattern(re::engine::Plugin self, ...)
-CODE:
-    SvREFCNT_inc(self->pattern);
-    RETVAL = self->pattern;
-OUTPUT:
-    RETVAL
+PPCODE:
+    XPUSHs(self->pattern);
 
-SV *
+void
 str(re::engine::Plugin self, ...)
-CODE:
-    SvREFCNT_inc(self->str);
-    RETVAL = self->str;
-OUTPUT:
-    RETVAL
+PPCODE:
+    XPUSHs(self->str);
 
-char*
+void
 mod(re::engine::Plugin self, ...)
 PPCODE:
     /* /i */
@@ -394,50 +636,58 @@ PPCODE:
       XPUSHs(&PL_sv_yes);
     }
 
-SV *
+void
 stash(re::engine::Plugin self, ...)
-PREINIT:
-    SV * stash;
-CODE:
+PPCODE:
     if (items > 1) {
-        self->stash = sv_mortalcopy(ST(1));
+        self->stash = ST(1);
         SvREFCNT_inc(self->stash);
+        XSRETURN_EMPTY;
+    } else {
+        XPUSHs(self->stash);
     }
-    SvREFCNT_inc(self->stash);
-    RETVAL = self->stash;
-OUTPUT:
-    RETVAL
 
-SV *
+void
 minlen(re::engine::Plugin self, ...)
-CODE:
+PPCODE:
     if (items > 1) {
         self->rx->minlen = (I32)SvIV(ST(1));
+        XSRETURN_EMPTY;
+    } else {
+        if (self->rx->minlen) {
+            XPUSHs(sv_2mortal(newSViv(self->rx->minlen)));
+        } else {
+            XPUSHs(sv_2mortal(&PL_sv_undef));
+        }
     }
 
-    RETVAL = self->rx->minlen ? newSViv(self->rx->minlen) : &PL_sv_undef;
-OUTPUT:
-    RETVAL
-
-SV *
+void
 gofs(re::engine::Plugin self, ...)
-CODE:
+PPCODE:
     if (items > 1) {
         self->rx->gofs = (U32)SvIV(ST(1));
+        XSRETURN_EMPTY;
+    } else {
+        if (self->rx->gofs) {
+            XPUSHs(sv_2mortal(newSVuv(self->rx->gofs)));
+        } else {
+            XPUSHs(sv_2mortal(&PL_sv_undef));
+        }
     }
-    RETVAL = self->rx->gofs ? newSVuv(self->rx->gofs) : &PL_sv_undef;
-OUTPUT:
-    RETVAL
 
-SV *
+void
 nparens(re::engine::Plugin self, ...)
-CODE:
+PPCODE:
     if (items > 1) {
         self->rx->nparens = (U32)SvIV(ST(1));
+        XSRETURN_EMPTY;
+    } else {
+        if (self->rx->nparens) {
+            XPUSHs(sv_2mortal(newSVuv(self->rx->nparens)));
+        } else {
+            XPUSHs(sv_2mortal(&PL_sv_undef));
+        }
     }
-    RETVAL = self->rx->nparens ? newSVuv(self->rx->nparens) : &PL_sv_undef;
-OUTPUT:
-    RETVAL
 
 void
 _num_capture_buff_FETCH(re::engine::Plugin self, ...)
@@ -463,6 +713,70 @@ PPCODE:
         SvREFCNT_inc(self->cb_num_capture_buff_LENGTH);
     }
 
+void
+_named_capture_buff_FETCH(re::engine::Plugin self, ...)
+PPCODE:
+    if (items > 1) {
+        self->cb_named_capture_buff_FETCH = ST(1);
+        SvREFCNT_inc(self->cb_named_capture_buff_FETCH);
+    }
+
+void
+_named_capture_buff_STORE(re::engine::Plugin self, ...)
+PPCODE:
+    if (items > 1) {
+        self->cb_named_capture_buff_STORE = ST(1);
+        SvREFCNT_inc(self->cb_named_capture_buff_STORE);
+    }
+
+void
+_named_capture_buff_DELETE(re::engine::Plugin self, ...)
+PPCODE:
+    if (items > 1) {
+        self->cb_named_capture_buff_DELETE = ST(1);
+        SvREFCNT_inc(self->cb_named_capture_buff_DELETE);
+    }
+
+void
+_named_capture_buff_CLEAR(re::engine::Plugin self, ...)
+PPCODE:
+    if (items > 1) {
+        self->cb_named_capture_buff_CLEAR = ST(1);
+        SvREFCNT_inc(self->cb_named_capture_buff_CLEAR);
+    }
+
+void
+_named_capture_buff_EXISTS(re::engine::Plugin self, ...)
+PPCODE:
+    if (items > 1) {
+        self->cb_named_capture_buff_EXISTS = ST(1);
+        SvREFCNT_inc(self->cb_named_capture_buff_EXISTS);
+    }
+
+void
+_named_capture_buff_FIRSTKEY(re::engine::Plugin self, ...)
+PPCODE:
+    if (items > 1) {
+        self->cb_named_capture_buff_FIRSTKEY = ST(1);
+        SvREFCNT_inc(self->cb_named_capture_buff_FIRSTKEY);
+    }
+
+void
+_named_capture_buff_NEXTKEY(re::engine::Plugin self, ...)
+PPCODE:
+    if (items > 1) {
+        self->cb_named_capture_buff_NEXTKEY = ST(1);
+        SvREFCNT_inc(self->cb_named_capture_buff_NEXTKEY);
+    }
+
+void
+_named_capture_buff_SCALAR(re::engine::Plugin self, ...)
+PPCODE:
+    if (items > 1) {
+        self->cb_named_capture_buff_SCALAR = ST(1);
+        SvREFCNT_inc(self->cb_named_capture_buff_SCALAR);
+    }
+
 void
 ENGINE()
 PPCODE:
diff --git a/named-capture.patch b/named-capture.patch
new file mode 100644 (file)
index 0000000..2cbcb8c
--- /dev/null
@@ -0,0 +1,1527 @@
+diff --git a/embed.fnc b/embed.fnc
+index fec5643..643c652 100644
+--- a/embed.fnc
++++ b/embed.fnc
+@@ -694,7 +694,15 @@ Ap        |I32    |regexec_flags  |NN REGEXP * const rx|NN char* stringarg \
+                               |NN SV* screamer|NULLOK void* data|U32 flags
+ ApR   |regnode*|regnext       |NN regnode* p
+-EXp   |SV*|reg_named_buff_fetch       |NN REGEXP * const rx|NN SV * const key|const U32 flags
++EXp |SV*|reg_named_buff_fetch    |NN REGEXP * const rx|NN SV * const namesv|const U32 flags
++EXp |void|reg_named_buff_store   |NN REGEXP * const rx|NN SV * const key \
++                                 |NN SV * const value|const U32 flags
++EXp |void|reg_named_buff_delete  |NN REGEXP * const rx|NN SV * const key|const U32 flags
++EXp |void|reg_named_buff_clear   |NN REGEXP * const rx|const U32 flags
++EXp |bool|reg_named_buff_exists  |NN REGEXP * const rx|NN SV * const key|const U32 flags
++EXp |SV*|reg_named_buff_firstkey |NN REGEXP * const rx|const U32 flags
++EXp |SV*|reg_named_buff_nextkey  |NN REGEXP * const rx|NN SV * const lastkey|const U32 flags
++EXp |SV*|reg_named_buff_scalar   |NN REGEXP * const rx|const U32 flags
+ EXp   |void|reg_numbered_buff_fetch|NN REGEXP * const rx|const I32 paren|NULLOK SV * const sv
+ EXp   |void|reg_numbered_buff_store|NN REGEXP * const rx|const I32 paren|NULLOK SV const * const value
+diff --git a/embed.h b/embed.h
+index 8e0ecba..aa0aa72 100644
+--- a/embed.h
++++ b/embed.h
+@@ -705,6 +705,13 @@
+ #define regnext                       Perl_regnext
+ #if defined(PERL_CORE) || defined(PERL_EXT)
+ #define reg_named_buff_fetch  Perl_reg_named_buff_fetch
++#define reg_named_buff_store  Perl_reg_named_buff_store
++#define reg_named_buff_delete Perl_reg_named_buff_delete
++#define reg_named_buff_clear  Perl_reg_named_buff_clear
++#define reg_named_buff_exists Perl_reg_named_buff_exists
++#define reg_named_buff_firstkey       Perl_reg_named_buff_firstkey
++#define reg_named_buff_nextkey        Perl_reg_named_buff_nextkey
++#define reg_named_buff_scalar Perl_reg_named_buff_scalar
+ #endif
+ #if defined(PERL_CORE) || defined(PERL_EXT)
+ #define reg_numbered_buff_fetch       Perl_reg_numbered_buff_fetch
+@@ -2981,6 +2988,13 @@
+ #define regnext(a)            Perl_regnext(aTHX_ a)
+ #if defined(PERL_CORE) || defined(PERL_EXT)
+ #define reg_named_buff_fetch(a,b,c)   Perl_reg_named_buff_fetch(aTHX_ a,b,c)
++#define reg_named_buff_store(a,b,c,d) Perl_reg_named_buff_store(aTHX_ a,b,c,d)
++#define reg_named_buff_delete(a,b,c)  Perl_reg_named_buff_delete(aTHX_ a,b,c)
++#define reg_named_buff_clear(a,b)     Perl_reg_named_buff_clear(aTHX_ a,b)
++#define reg_named_buff_exists(a,b,c)  Perl_reg_named_buff_exists(aTHX_ a,b,c)
++#define reg_named_buff_firstkey(a,b)  Perl_reg_named_buff_firstkey(aTHX_ a,b)
++#define reg_named_buff_nextkey(a,b,c) Perl_reg_named_buff_nextkey(aTHX_ a,b,c)
++#define reg_named_buff_scalar(a,b)    Perl_reg_named_buff_scalar(aTHX_ a,b)
+ #endif
+ #if defined(PERL_CORE) || defined(PERL_EXT)
+ #define reg_numbered_buff_fetch(a,b,c)        Perl_reg_numbered_buff_fetch(aTHX_ a,b,c)
+diff --git a/ext/re/re.xs b/ext/re/re.xs
+index 1344065..fe59940 100644
+--- a/ext/re/re.xs
++++ b/ext/re/re.xs
+@@ -30,8 +30,22 @@ extern void my_reg_numbered_buff_store(pTHX_ REGEXP * const rx, const I32 paren,
+ extern I32    my_reg_numbered_buff_length(pTHX_ REGEXP * const rx,
+                                           const SV * const sv, const I32 paren);
+-extern SV*    my_reg_named_buff_fetch(pTHX_ REGEXP * const rx, SV * const key,
+-                                      const U32 flags);
++extern SV*    my_reg_named_buff_fetch(pTHX_ REGEXP * const, SV * const,
++                                                                      const U32);
++extern void   my_reg_named_buff_store(pTHX_ REGEXP * const rx,
++                                                                      SV * const key, SV * const value,
++                                                                      const U32 flags);
++extern void   my_reg_named_buff_delete(pTHX_ REGEXP * const rx,
++                                                                       SV * const key, const U32 flags);
++extern void   my_reg_named_buff_clear(pTHX_ REGEXP * const rx, const U32 flags);
++extern bool   my_reg_named_buff_exists(pTHX_ REGEXP * const rx,
++                                                                       SV * const key, const U32 flags);
++extern SV*    my_reg_named_buff_firstkey(pTHX_ REGEXP * const rx,
++                                                                         const U32 flags);
++extern SV*    my_reg_named_buff_nextkey(pTHX_ REGEXP * const rx,
++                                                                        SV * const lastkey, const U32 flags);
++extern SV*    my_reg_named_buff_scalar(pTHX_ REGEXP * const rx,
++                                                                       const U32 flags);
+ extern SV*      my_reg_qr_package(pTHX_ REGEXP * const rx);
+ #if defined(USE_ITHREADS)
+@@ -52,6 +66,13 @@ const struct regexp_engine my_reg_engine = {
+         my_reg_numbered_buff_store,
+         my_reg_numbered_buff_length,
+         my_reg_named_buff_fetch,
++        my_reg_named_buff_store,
++        my_reg_named_buff_delete,
++        my_reg_named_buff_clear,
++        my_reg_named_buff_exists,
++        my_reg_named_buff_firstkey,
++        my_reg_named_buff_nextkey,
++        my_reg_named_buff_scalar,
+         my_reg_qr_package,
+ #if defined(USE_ITHREADS)
+         my_regdupe 
+diff --git a/ext/re/re_top.h b/ext/re/re_top.h
+index 5570ed7..23ee654 100644
+--- a/ext/re/re_top.h
++++ b/ext/re/re_top.h
+@@ -20,6 +20,13 @@
+ #define Perl_reg_numbered_buff_store  my_reg_numbered_buff_store
+ #define Perl_reg_numbered_buff_length  my_reg_numbered_buff_length
+ #define Perl_reg_named_buff_fetch  my_reg_named_buff_fetch
++#define Perl_reg_named_buff_store  my_reg_named_buff_store
++#define Perl_reg_named_buff_delete  my_reg_named_buff_delete
++#define Perl_reg_named_buff_clear  my_reg_named_buff_clear
++#define Perl_reg_named_buff_exists  my_reg_named_buff_exists
++#define Perl_reg_named_buff_firstkey  my_reg_named_buff_firstkey
++#define Perl_reg_named_buff_nextkey  my_reg_named_buff_nextkey
++#define Perl_reg_named_buff_scalar  my_reg_named_buff_scalar
+ #define Perl_reg_qr_package        my_reg_qr_package
+ #define PERL_NO_GET_CONTEXT
+diff --git a/ext/re/t/re_funcs.t b/ext/re/t/re_funcs.t
+index 0d9092a..fa3e11b 100644
+--- a/ext/re/t/re_funcs.t
++++ b/ext/re/t/re_funcs.t
+@@ -40,23 +40,17 @@ use re qw(is_regexp regexp_pattern regmust
+     is($floating,undef,"Regmust anchored - ref");
+ }
+-
+ if ('1234'=~/(?:(?<A>\d)|(?<C>!))(?<B>\d)(?<A>\d)(?<B>\d)/){
+     my @names = sort +regnames();
+     is("@names","A B","regnames");
++    my @names = sort +regnames(0);
++    is("@names","A B","regnames");
+     @names = sort +regnames(1);
+     is("@names","A B C","regnames");
+     is(join("", @{regname("A",1)}),"13");
+     is(join("", @{regname("B",1)}),"24");    
+     {
+         if ('foobar'=~/(?<foo>foo)(?<bar>bar)/) {
+-            regnames_iterinit();
+-            my @res;
+-            while (defined(my $key=regnames_iternext)) {
+-                push @res,$key;
+-            }
+-            @res=sort @res;
+-            is("@res","bar foo");
+             is(regnames_count(),2);
+         } else {
+             ok(0); ok(0);
+diff --git a/global.sym b/global.sym
+index c5181b4..0714bff 100644
+--- a/global.sym
++++ b/global.sym
+@@ -406,6 +406,13 @@ Perl_re_intuit_string
+ Perl_regexec_flags
+ Perl_regnext
+ Perl_reg_named_buff_fetch
++Perl_reg_named_buff_store
++Perl_reg_named_buff_delete
++Perl_reg_named_buff_clear
++Perl_reg_named_buff_exists
++Perl_reg_named_buff_firstkey
++Perl_reg_named_buff_nextkey
++Perl_reg_named_buff_scalar
+ Perl_reg_numbered_buff_fetch
+ Perl_reg_numbered_buff_store
+ Perl_reg_numbered_buff_length
+diff --git a/gv.c b/gv.c
+index 17f754f..8f98f00 100644
+--- a/gv.c
++++ b/gv.c
+@@ -1014,7 +1014,7 @@ Perl_gv_fetchpvn_flags(pTHX_ const char *nambeg, STRLEN full_len, I32 flags,
+               if (*name == '!')
+                   require_tie_mod(gv, "!", newSVpvs("Errno"), "TIEHASH", 1);
+               else if (*name == '-' || *name == '+')
+-                  require_tie_mod(gv, name, newSVpvs("Tie::Hash::NamedCapture"), "FETCH", 0);
++                  require_tie_mod(gv, name, newSVpvs("Tie::Hash::NamedCapture"), "TIEHASH", 0);
+           }
+       }
+       return gv;
+@@ -1224,7 +1224,7 @@ Perl_gv_fetchpvn_flags(pTHX_ const char *nambeg, STRLEN full_len, I32 flags,
+             SvREADONLY_on(av);
+             if (sv_type == SVt_PVHV || sv_type == SVt_PVGV)
+-                require_tie_mod(gv, name, newSVpvs("Tie::Hash::NamedCapture"), "FETCH", 0);
++                require_tie_mod(gv, name, newSVpvs("Tie::Hash::NamedCapture"), "TIEHASH", 0);
+             break;
+       }
+diff --git a/lib/Tie/Hash/NamedCapture.pm b/lib/Tie/Hash/NamedCapture.pm
+index 73bc20b..66cf1b5 100644
+--- a/lib/Tie/Hash/NamedCapture.pm
++++ b/lib/Tie/Hash/NamedCapture.pm
+@@ -1,52 +1,19 @@
+ package Tie::Hash::NamedCapture;
+-use strict;
+-use warnings;
++our $VERSION = "0.06";
+-our $VERSION = "0.05";
++# The real meat implemented in XS in universal.c in the core, but this
++# method was left behind because gv.c expects a Purl-Perl method in
++# this package when it loads the tie magic for %+ and %-
+-sub TIEHASH {
+-    my $classname = shift;
+-    my %opts = @_;
+-
+-    my $self = bless { all => $opts{all} }, $classname;
+-    return $self;
+-}
+-
+-sub FETCH {
+-    return re::regname($_[1],$_[0]->{all});
+-}
+-
+-sub STORE {
+-    require Carp;
+-    Carp::croak("STORE forbidden: hashes tied to ",__PACKAGE__," are read-only.");
+-}
+-
+-sub FIRSTKEY {
+-    re::regnames_iterinit();
+-    return $_[0]->NEXTKEY;
+-}
++# These should match the #defines in regexp.h
++sub RXf_HASH_ONE () { 0x00000001 } # %+
++sub RXf_HASH_ALL () { 0x00000002 } # %-
+-sub NEXTKEY {
+-    return re::regnames_iternext($_[0]->{all});
+-}
+-
+-sub EXISTS {
+-    return defined re::regname( $_[1], $_[0]->{all});
+-}
+-
+-sub DELETE {
+-    require Carp;
+-    Carp::croak("DELETE forbidden: hashes tied to ",__PACKAGE__," are read-only");
+-}
+-
+-sub CLEAR {
+-    require Carp;
+-    Carp::croak("CLEAR forbidden: hashes tied to ",__PACKAGE__," are read-only");
+-}
+-
+-sub SCALAR {
+-    return scalar re::regnames($_[0]->{all});
++sub TIEHASH {
++    my ($pkg, %arg) = @_;
++    my $flag = $arg{all} ? RXf_HASH_ALL : RXf_HASH_ONE;
++    bless \$flag => $pkg;
+ }
+ tie %+, __PACKAGE__;
+@@ -91,6 +58,7 @@ buffers that have captured (and that are thus associated to defined values).
+ =head1 SEE ALSO
+-L<re>, L<perlmodlib/Pragmatic Modules>, L<perlvar/"%+">, L<perlvar/"%-">.
++L<perlreapi>, L<re>, L<perlmodlib/Pragmatic Modules>, L<perlvar/"%+">,
++L<perlvar/"%-">.
+ =cut
+diff --git a/mg.c b/mg.c
+index 77ae021..bc08d4a 100644
+--- a/mg.c
++++ b/mg.c
+@@ -603,15 +603,15 @@ Perl_magic_len(pTHX_ SV *sv, MAGIC *mg)
+     }
+     case '`':
+       do_prematch:
+-      paren = -2;
++      paren = RXrf_PREMATCH;
+       goto maybegetparen;
+     case '\'':
+       do_postmatch:
+-      paren = -1;
++      paren = RXrf_POSTMATCH;
+       goto maybegetparen;
+     case '&':
+       do_match:
+-      paren = 0;
++      paren = RXrf_MATCH;
+       goto maybegetparen;
+     case '1': case '2': case '3': case '4':
+     case '5': case '6': case '7': case '8': case '9':
+@@ -2235,15 +2235,15 @@ Perl_magic_set(pTHX_ SV *sv, MAGIC *mg)
+           goto do_match;
+     case '`': /* ${^PREMATCH} caught below */
+       do_prematch:
+-      paren = -2;
++      paren = RXrf_PREMATCH;
+       goto setparen;
+     case '\'': /* ${^POSTMATCH} caught below */
+       do_postmatch:
+-      paren = -1;
++      paren = RXrf_POSTMATCH;
+       goto setparen;
+     case '&':
+       do_match:
+-      paren = 0;
++      paren = RXrf_MATCH;
+       goto setparen;
+     case '1': case '2': case '3': case '4':
+     case '5': case '6': case '7': case '8': case '9':
+diff --git a/perl.h b/perl.h
+index 8cd8753..0a4aebf 100644
+--- a/perl.h
++++ b/perl.h
+@@ -231,6 +231,27 @@
+ #define CALLREG_NAMEDBUF_FETCH(rx,name,flags) \
+     CALL_FPTR((rx)->engine->named_buff_FETCH)(aTHX_ (rx),(name),(flags))
++#define CALLREG_NAMEDBUF_STORE(rx,key,value,flags) \
++    CALL_FPTR((rx)->engine->named_buff_STORE)(aTHX_ (rx),(key),(value),(flags))
++
++#define CALLREG_NAMEDBUF_DELETE(rx,key,flags) \
++    CALL_FPTR((rx)->engine->named_buff_DELETE)(aTHX_ (rx),(key),(flags))
++
++#define CALLREG_NAMEDBUF_CLEAR(rx,flags) \
++    CALL_FPTR((rx)->engine->named_buff_CLEAR)(aTHX_ (rx),(flags))
++
++#define CALLREG_NAMEDBUF_EXISTS(rx,key,flags) \
++    CALL_FPTR((rx)->engine->named_buff_EXISTS)(aTHX_ (rx),(key),(flags))
++
++#define CALLREG_NAMEDBUF_FIRSTKEY(rx,flags) \
++    CALL_FPTR((rx)->engine->named_buff_FIRSTKEY)(aTHX_ (rx),(flags))
++
++#define CALLREG_NAMEDBUF_NEXTKEY(rx,lastkey,flags) \
++    CALL_FPTR((rx)->engine->named_buff_NEXTKEY)(aTHX_ (rx),(lastkey),(flags))
++
++#define CALLREG_NAMEDBUF_SCALAR(rx,flags) \
++    CALL_FPTR((rx)->engine->named_buff_SCALAR)(aTHX_ (rx),(flags))
++
+ #define CALLREG_PACKAGE(rx) \
+     CALL_FPTR((rx)->engine->qr_package)(aTHX_ (rx))
+diff --git a/pod/perlreapi.pod b/pod/perlreapi.pod
+index 1a170ff..08ae8cd 100644
+--- a/pod/perlreapi.pod
++++ b/pod/perlreapi.pod
+@@ -26,6 +26,18 @@ structure of the following format:
+                                         const I32 paren);
+         SV*     (*named_buff_FETCH) (pTHX_ REGEXP * const rx, SV * const sv,
+                                      const U32 flags);
++        void    (*named_buff_STORE) (pTHX_ REGEXP * const rx, SV * const key,
++                                     SV * const value, const U32 flags);
++        void    (*named_buff_DELETE) (pTHX_ REGEXP * const rx,
++                                      SV * const key, const U32 flags);
++        void    (*named_buff_CLEAR) (pTHX_ REGEXP * const rx, const U32 flags);
++        bool    (*named_buff_EXISTS) (pTHX_ REGEXP * const rx,
++                                      SV * const key, const U32 flags);
++        SV*     (*named_buff_FIRSTKEY) (pTHX_ REGEXP * const rx, const U32 flags);
++        SV*     (*named_buff_NEXTKEY) (pTHX_ REGEXP * const rx,
++                                       SV * const lastkey, const U32 flags);
++        SV*     (*named_buff_SCALAR) (pTHX_ REGEXP * const rx,
++                                      const U32 flags);
+         SV*     (*qr_package)(pTHX_ REGEXP * const rx);
+     #ifdef USE_ITHREADS
+         void*   (*dupe) (pTHX_ REGEXP * const rx, CLONE_PARAMS *param);
+@@ -186,38 +198,45 @@ can release any resources pointed to by the C<pprivate> member of the
+ regexp structure. This is only responsible for freeing private data;
+ perl will handle releasing anything else contained in the regexp structure.
+-=head2 numbered_buff_FETCH
++=head2 Numbered capture callbacks
+-    void numbered_buff_FETCH(pTHX_ REGEXP * const rx, const I32 paren,
+-                             SV * const sv);
+-
+-Called to get the value of C<$`>, C<$'>, C<$&> (and their named
+-equivalents, see L<perlvar>) and the numbered capture buffers (C<$1>,
+-C<$2>, ...).
++Called to get/set the value of C<$`>, C<$'>, C<$&> and their named
++equivalents, ${^PREMATCH}, ${^POSTMATCH} and $^{MATCH}, as well as the
++numbered capture buffers (C<$1>, C<$2>, ...).
+ The C<paren> paramater will be C<-2> for C<$`>, C<-1> for C<$'>, C<0>
+ for C<$&>, C<1> for C<$1> and so forth.
+-C<sv> should be set to the scalar to return, the scalar is passed as
+-an argument rather than being returned from the function because when
+-it's called perl already has a scalar to store the value, creating
+-another one would be redundant. The scalar can be set with
+-C<sv_setsv>, C<sv_setpvn> and friends, see L<perlapi>.
++The names have been chosen by analogy with L<Tie::Scalar> methods
++names with an additional B<LENGTH> callback for efficiency. However
++named capture variables are currently not tied internally but
++implemented via magic.
++
++=head3 numbered_buff_FETCH
++
++    void numbered_buff_FETCH(pTHX_ REGEXP * const rx, const I32 paren,
++                             SV * const sv);
++
++Fetch a specified numbered capture. C<sv> should be set to the scalar
++to return, the scalar is passed as an argument rather than being
++returned from the function because when it's called perl already has a
++scalar to store the value, creating another one would be
++redundant. The scalar can be set with C<sv_setsv>, C<sv_setpvn> and
++friends, see L<perlapi>.
+ This callback is where perl untaints its own capture variables under
+ taint mode (see L<perlsec>). See the C<Perl_reg_numbered_buff_get>
+ function in F<regcomp.c> for how to untaint capture variables if
+ that's something you'd like your engine to do as well.
+-=head2 numbered_buff_STORE
++=head3 numbered_buff_STORE
+     void    (*numbered_buff_STORE) (pTHX_ REGEXP * const rx, const I32 paren,
+                                     SV const * const value);
+-Called to set the value of a numbered capture variable. C<paren> is
+-the paren number (see the L<mapping|/numbered_buff_FETCH> above) and
+-C<value> is the scalar that is to be used as the new value. It's up to
+-the engine to make sure this is used as the new value (or reject it).
++Set the value of a numbered capture variable. C<value> is the scalar
++that is to be used as the new value. It's up to the engine to make
++sure this is used as the new value (or reject it).
+ Example:
+@@ -262,19 +281,19 @@ behave in the same situation:
+ Because C<$sv> is C<undef> when the C<y///> operator is applied to it
+ the transliteration won't actually execute and the program won't
+-C<die>. This is different to how 5.8 behaved since the capture
+-variables were READONLY variables then, now they'll just die on
+-assignment in the default engine.
++C<die>. This is different to how 5.8 and earlier versions behaved
++since the capture variables were READONLY variables then, now they'll
++just die when assigned to in the default engine.
+-=head2 numbered_buff_LENGTH
++=head3 numbered_buff_LENGTH
+     I32 numbered_buff_LENGTH (pTHX_ REGEXP * const rx, const SV * const sv,
+                               const I32 paren);
+ Get the C<length> of a capture variable. There's a special callback
+ for this so that perl doesn't have to do a FETCH and run C<length> on
+-the result, since the length is (in perl's case) known from a memory
+-offset this is much more efficient:
++the result, since the length is (in perl's case) known from an offset
++stored in C<<rx->offs> this is much more efficient:
+     I32 s1  = rx->offs[paren].start;
+     I32 s2  = rx->offs[paren].end;
+@@ -284,14 +303,79 @@ This is a little bit more complex in the case of UTF-8, see what
+ C<Perl_reg_numbered_buff_length> does with
+ L<is_utf8_string_loclen|perlapi/is_utf8_string_loclen>.
+-=head2 named_buff_FETCH
++=head2 Named capture callbacks
++
++Called to get/set the value of C<%+> and C<%->. If C<%+> is being
++operated on C<flags & RXf_HASH_ONE> will be true and C<flags &
++RXf_HASH_ALL> will be true for C<%->. There's also an additional flag
++for the L<SCALAR|/named_buff_SCALAR> callback, see below.
++
++This is implemented with a real tied interface via
++L<Tie::Hash::NamedCapture>, its methods call back into these
++functions, the usage of L<Tie::Hash::NamedCapture> for this purpose
++might change in future releases. For instance this might be
++implemented by magic instead (would need an extension to mgvtbl).
++
++Since these functions are just C level wrappers for the interface
++described in L<Tie::Hash> their arguments and return values are as
++described there, only with C prototypes.
++
++=head3 named_buff_FETCH
+     SV* named_buff_FETCH(pTHX_ REGEXP * const rx, SV * const key,
+                           const U32 flags);
+-Called to get the value of key in the C<%+> and C<%-> hashes, C<key>
+-is the hash key being requested and if C<flags & 1> is true C<%-> is
+-being requested (and C<%+> if it's not).
++Get an entry.
++
++=head3 named_buff_STORE
++
++    void named_buff_STORE (pTHX_ REGEXP * const rx, SV * const key,
++                           SV * const value, const U32 flags);
++
++Store a value.
++
++=head3 named_buff_DELETE
++
++    void named_buff_DELETE (pTHX_ REGEXP * const rx,
++                            SV * const key, const U32 flags);
++
++Delete an entry.
++
++=head3 named_buff_CLEAR
++
++    void named_buff_CLEAR (pTHX_ REGEXP * const rx, const U32 flags);
++
++Clear the hash.
++
++=head3 named_buff_EXISTS
++
++    bool named_buff_EXISTS (pTHX_ REGEXP * const rx,
++                            SV * const key, const U32 flags);
++
++Check whether an entry C<exists>.
++
++=head3 named_buff_FIRSTKEY
++
++    SV*     (*named_buff_FIRSTKEY) (pTHX_ REGEXP * const rx, const U32 flags);
++
++Begin iterating the hash.
++
++=head3 named_buff_NEXTKEY
++
++    SV* named_buff_NEXTKEY (pTHX_ REGEXP * const rx,
++                            SV * const lastkey, const U32 flags);
++
++Get the next entry in the hash.
++
++=head3 named_buff_SCALAR
++
++    SV* named_buff_SCALAR (pTHX_ REGEXP * const rx, const U32 flags);
++
++Return what the hash evaluates to in C<scalar> context.
++
++This will also be called by L<re::regnames_count|re> to get the total
++number of named capture buffers defined for the pattern, in this case
++C<flags & RXf_HASH_COUNT> will be true.
+ =head2 qr_package
+@@ -333,7 +417,7 @@ following snippet:
+             SvTYPE(sv) == SVt_PVMG &&
+             (mg = mg_find(sv, PERL_MAGIC_qr))) /* assignment deliberate */
+         {
+-            re = (REGEXP *)mg->mg_obj; 
++            re = (REGEXP *)mg->mg_obj;
+         }
+ Or use the (CURRENTLY UNDOCUMENETED!) C<Perl_get_re_arg> function:
+@@ -448,8 +532,9 @@ TODO, see L<http://www.mail-archive.com/perl5-changes@perl.org/msg17328.html>
+ =head2 C<extflags>
+-This will be used by perl to see what flags the regexp was compiled with, this
+-will normally be set to the value of the flags parameter on L</comp>.
++This will be used by perl to see what flags the regexp was compiled
++with, this will normally be set to the value of the flags parameter by
++the L<comp|/comp> callback.
+ =head2 C<minlen> C<minlenret>
+@@ -479,7 +564,9 @@ Left offset from pos() to start match at.
+ =head2 C<substrs>
+-TODO: document
++Substring data about strings that must appear in the final match. This
++is currently only used internally by perl's engine for but might be
++used in the future for all engines for optimisations like C<minlen>.
+ =head2 C<nparens>, C<lasparen>, and C<lastcloseparen>
+@@ -490,7 +577,7 @@ the last close paren to be entered.
+ =head2 C<intflags>
+ The engine's private copy of the flags the pattern was compiled with. Usually
+-this is the same as C<extflags> unless the engine chose to modify one of them
++this is the same as C<extflags> unless the engine chose to modify one of them.
+ =head2 C<pprivate>
+@@ -520,8 +607,18 @@ C<$paren >= 1>.
+ =head2 C<precomp> C<prelen>
+-Used for debugging purposes. C<precomp> holds a copy of the pattern
+-that was compiled and C<prelen> its length.
++Used for optimisations. C<precomp> holds a copy of the pattern that
++was compiled and C<prelen> its length. When a new pattern is to be
++compiled (such as inside a loop) the internal C<regcomp> operator
++checks whether the last compiled C<REGEXP>'s C<precomp> and C<prelen>
++are equivalent to the new one, and if so uses the old pattern instead
++of compiling a new one.
++
++The relevant snippet from C<Perl_pp_regcomp>:
++
++      if (!re || !re->precomp || re->prelen != (I32)len ||
++          memNE(re->precomp, t, len))
++        /* Compile a new pattern */
+ =head2 C<paren_names>
+@@ -563,11 +660,11 @@ inline modifiers it's best to have C<qr//> stringify to the supplied pattern,
+ note that this will create invalid patterns in cases such as:
+     my $x = qr/a|b/;  # "a|b"
+-    my $y = qr/c/;    # "c"
++    my $y = qr/c/i;   # "c"
+     my $z = qr/$x$y/; # "a|bc"
+-There's no solution for such problems other than making the custom engine
+-understand some for of inline modifiers.
++There's no solution for this problem other than making the custom
++engine understand a construct like C<(?:)>.
+ The C<Perl_reg_stringify> in F<regcomp.c> does the stringification work.
+diff --git a/proto.h b/proto.h
+index dee615f..02b24d5 100644
+--- a/proto.h
++++ b/proto.h
+@@ -1893,10 +1893,36 @@ PERL_CALLCONV regnode* Perl_regnext(pTHX_ regnode* p)
+                       __attribute__nonnull__(pTHX_1);
+-PERL_CALLCONV SV*     Perl_reg_named_buff_fetch(pTHX_ REGEXP * const rx, SV * const key, const U32 flags)
++PERL_CALLCONV SV*     Perl_reg_named_buff_fetch(pTHX_ REGEXP * const rx, SV * const namesv, const U32 flags)
+                       __attribute__nonnull__(pTHX_1)
+                       __attribute__nonnull__(pTHX_2);
++PERL_CALLCONV void    Perl_reg_named_buff_store(pTHX_ REGEXP * const rx, SV * const key, SV * const value, const U32 flags)
++                      __attribute__nonnull__(pTHX_1)
++                      __attribute__nonnull__(pTHX_2)
++                      __attribute__nonnull__(pTHX_3);
++
++PERL_CALLCONV void    Perl_reg_named_buff_delete(pTHX_ REGEXP * const rx, SV * const key, const U32 flags)
++                      __attribute__nonnull__(pTHX_1)
++                      __attribute__nonnull__(pTHX_2);
++
++PERL_CALLCONV void    Perl_reg_named_buff_clear(pTHX_ REGEXP * const rx, const U32 flags)
++                      __attribute__nonnull__(pTHX_1);
++
++PERL_CALLCONV bool    Perl_reg_named_buff_exists(pTHX_ REGEXP * const rx, SV * const key, const U32 flags)
++                      __attribute__nonnull__(pTHX_1)
++                      __attribute__nonnull__(pTHX_2);
++
++PERL_CALLCONV SV*     Perl_reg_named_buff_firstkey(pTHX_ REGEXP * const rx, const U32 flags)
++                      __attribute__nonnull__(pTHX_1);
++
++PERL_CALLCONV SV*     Perl_reg_named_buff_nextkey(pTHX_ REGEXP * const rx, SV * const lastkey, const U32 flags)
++                      __attribute__nonnull__(pTHX_1)
++                      __attribute__nonnull__(pTHX_2);
++
++PERL_CALLCONV SV*     Perl_reg_named_buff_scalar(pTHX_ REGEXP * const rx, const U32 flags)
++                      __attribute__nonnull__(pTHX_1);
++
+ PERL_CALLCONV void    Perl_reg_numbered_buff_fetch(pTHX_ REGEXP * const rx, const I32 paren, SV * const sv)
+                       __attribute__nonnull__(pTHX_1);
+diff --git a/regcomp.c b/regcomp.c
+index baa5d99..c144d76 100644
+--- a/regcomp.c
++++ b/regcomp.c
+@@ -228,7 +228,7 @@ typedef struct RExC_state_t {
+   
+     /FOO[xX]A.*B[xX]BAR/
+     
+-  Both 'FOO' and 'A' are fixed strings. Both 'B' and 'BAR' are floating
++v  Both 'FOO' and 'A' are fixed strings. Both 'B' and 'BAR' are floating
+   strings (because they follow a .* construct). study_chunk will identify
+   both FOO and BAR as being the longest fixed and floating strings respectively.
+   
+@@ -4800,7 +4800,7 @@ Perl_reg_named_buff_fetch(pTHX_ REGEXP * const rx, SV * const namesv, const U32
+ {
+     AV *retarray = NULL;
+     SV *ret;
+-    if (flags & 1) 
++    if (flags & RXf_HASH_ALL)
+         retarray=newAV();
+     if (rx && rx->paren_names) {
+@@ -4810,9 +4810,9 @@ Perl_reg_named_buff_fetch(pTHX_ REGEXP * const rx, SV * const namesv, const U32
+             SV* sv_dat=HeVAL(he_str);
+             I32 *nums=(I32*)SvPVX(sv_dat);
+             for ( i=0; i<SvIVX(sv_dat); i++ ) {
+-              if ((I32)(rx->nparens) >= nums[i]
+-                      && rx->offs[nums[i]].start != -1
+-                      && rx->offs[nums[i]].end != -1)
++                if ((I32)(rx->nparens) >= nums[i]
++                    && rx->offs[nums[i]].start != -1
++                    && rx->offs[nums[i]].end != -1)
+                 {
+                     ret = newSVpvs("");
+                     CALLREG_NUMBUF_FETCH(rx,nums[i],ret);
+@@ -4827,13 +4827,116 @@ Perl_reg_named_buff_fetch(pTHX_ REGEXP * const rx, SV * const namesv, const U32
+                 }
+             }
+             if (retarray)
+-                return (SV*)retarray;
++                return newRV((SV*)retarray);
+         }
+     }
+     return NULL;
+ }
+ void
++Perl_reg_named_buff_store(pTHX_ REGEXP * const rx, SV * const key,
++                          SV * const value, const U32 flags)
++{
++    PERL_UNUSED_ARG(rx);
++    PERL_UNUSED_ARG(key);
++    PERL_UNUSED_ARG(value);
++    PERL_UNUSED_ARG(flags);
++
++    Perl_croak(aTHX_ PL_no_modify);
++}
++
++void
++Perl_reg_named_buff_delete(pTHX_ REGEXP * const rx, SV * const key, const U32 flags)
++{
++    PERL_UNUSED_ARG(rx);
++    PERL_UNUSED_ARG(key);
++    PERL_UNUSED_ARG(flags);
++
++    Perl_croak(aTHX_ PL_no_modify);
++}
++
++void
++Perl_reg_named_buff_clear(pTHX_ REGEXP * const rx, const U32 flags)
++{
++    PERL_UNUSED_ARG(rx);
++    PERL_UNUSED_ARG(flags);
++
++    Perl_croak(aTHX_ PL_no_modify);
++}
++
++bool
++Perl_reg_named_buff_exists(pTHX_ REGEXP * const rx, SV * const key,
++                           const U32 flags)
++{
++    PERL_UNUSED_ARG(flags);
++
++    if (rx && rx->paren_names) {
++        return hv_exists_ent(rx->paren_names, key, 0);
++    } else {
++        return FALSE;
++    }
++}
++
++SV*
++Perl_reg_named_buff_firstkey(pTHX_ REGEXP * const rx, const U32 flags)
++{
++    PERL_UNUSED_ARG(flags);
++
++    (void)hv_iterinit(rx->paren_names);
++
++    return CALLREG_NAMEDBUF_NEXTKEY(rx, NULL, flags);
++}
++
++SV*
++Perl_reg_named_buff_nextkey(pTHX_ REGEXP * const rx, SV * const lastkey,
++                            const U32 flags)
++{
++    PERL_UNUSED_ARG(lastkey);
++    bool all = flags & RXf_HASH_ALL;
++
++    if (rx && rx->paren_names) {
++        HV *hv= rx->paren_names;
++        while (1) {
++            HE *temphe = hv_iternext_flags(hv,0);
++            if (temphe) {
++                IV i;
++                IV parno = 0;
++                SV* sv_dat = HeVAL(temphe);
++                I32 *nums = (I32*)SvPVX(sv_dat);
++                for ( i = 0; i < SvIVX(sv_dat); i++ ) {
++                    if ((I32)(rx->lastcloseparen) >= nums[i] &&
++                        rx->offs[nums[i]].start != -1 &&
++                        rx->offs[nums[i]].end != -1)
++                    {
++                        parno = nums[i];
++                        break;
++                    }
++                }
++                if (parno || all) {
++                    STRLEN len;
++                    char *pv = HePV(temphe, len);
++                    return newSVpvn(pv,len);
++                }
++            } else {
++                break;
++            }
++        }
++    }
++
++    return NULL;
++}
++
++SV*
++Perl_reg_named_buff_scalar(pTHX_ REGEXP * const rx, const U32 flags)
++{
++    PERL_UNUSED_ARG(flags);
++
++    if (rx && rx->paren_names)
++        return newSViv(HvTOTALKEYS(rx->paren_names));
++      return &PL_sv_undef;
++}
++
++void
+ Perl_reg_numbered_buff_fetch(pTHX_ REGEXP * const rx, const I32 paren, SV * const sv)
+ {
+     char *s = NULL;
+@@ -4845,13 +4948,13 @@ Perl_reg_numbered_buff_fetch(pTHX_ REGEXP * const rx, const I32 paren, SV * cons
+         return;
+     } 
+     else               
+-    if (paren == -2 && rx->offs[0].start != -1) {
++    if (paren == RXrf_PREMATCH && rx->offs[0].start != -1) {
+         /* $` */
+       i = rx->offs[0].start;
+       s = rx->subbeg;
+     }
+     else 
+-    if (paren == -1 && rx->offs[0].end != -1) {
++    if (paren == RXrf_POSTMATCH && rx->offs[0].end != -1) {
+         /* $' */
+       s = rx->subbeg + rx->offs[0].end;
+       i = rx->sublen - rx->offs[0].end;
+@@ -4929,7 +5032,8 @@ Perl_reg_numbered_buff_length(pTHX_ REGEXP * const rx, const SV * const sv,
+     /* Some of this code was originally in C<Perl_magic_len> in F<mg.c> */
+       switch (paren) {
+-      case -2: /* $` */
++      /* $` / ${^PREMATCH} */
++      case RXrf_PREMATCH:
+         if (rx->offs[0].start != -1) {
+                       i = rx->offs[0].start;
+                       if (i > 0) {
+@@ -4939,7 +5043,8 @@ Perl_reg_numbered_buff_length(pTHX_ REGEXP * const rx, const SV * const sv,
+                       }
+           }
+         return 0;
+-      case -1: /* $' */
++      /* $' / ${^POSTMATCH} */
++      case RXrf_POSTMATCH:
+           if (rx->offs[0].end != -1) {
+                       i = rx->sublen - rx->offs[0].end;
+                       if (i > 0) {
+@@ -4949,7 +5054,8 @@ Perl_reg_numbered_buff_length(pTHX_ REGEXP * const rx, const SV * const sv,
+                       }
+           }
+         return 0;
+-      default: /* $&, $1, $2, ... */
++      /* $& / ${^MATCH}, $1, $2, ... */
++      default:
+           if (paren <= (I32)rx->nparens &&
+             (s1 = rx->offs[paren].start) != -1 &&
+             (t1 = rx->offs[paren].end) != -1)
+diff --git a/regcomp.h b/regcomp.h
+index 33c3eef..5be7f32 100644
+--- a/regcomp.h
++++ b/regcomp.h
+@@ -473,6 +473,13 @@ EXTCONST regexp_engine PL_core_reg_engine = {
+         Perl_reg_numbered_buff_store,
+         Perl_reg_numbered_buff_length,
+         Perl_reg_named_buff_fetch,
++        Perl_reg_named_buff_store,
++        Perl_reg_named_buff_delete,
++        Perl_reg_named_buff_clear,
++        Perl_reg_named_buff_exists,
++        Perl_reg_named_buff_firstkey,
++        Perl_reg_named_buff_nextkey,
++        Perl_reg_named_buff_scalar,
+         Perl_reg_qr_package,
+ #if defined(USE_ITHREADS)        
+         Perl_regdupe_internal
+diff --git a/regexp.h b/regexp.h
+index 1f72112..31d264d 100644
+--- a/regexp.h
++++ b/regexp.h
+@@ -136,14 +136,40 @@ typedef struct regexp_engine {
+                                    SV const * const value);
+     I32     (*numbered_buff_LENGTH) (pTHX_ REGEXP * const rx, const SV * const sv,
+                                     const I32 paren);
+-    SV*     (*named_buff_FETCH) (pTHX_ REGEXP * const rx, SV * const key,
++    SV*     (*named_buff_FETCH) (pTHX_ REGEXP * const rx, SV * const sv,
+                                  const U32 flags);
++    void    (*named_buff_STORE) (pTHX_ REGEXP * const rx, SV * const key,
++                                 SV * const value, const U32 flags);
++    void    (*named_buff_DELETE) (pTHX_ REGEXP * const rx,
++                                  SV * const key, const U32 flags);
++    void    (*named_buff_CLEAR) (pTHX_ REGEXP * const rx, const U32 flags);
++    bool    (*named_buff_EXISTS) (pTHX_ REGEXP * const rx,
++                                  SV * const key, const U32 flags);
++    SV*     (*named_buff_FIRSTKEY) (pTHX_ REGEXP * const rx, const U32 flags);
++    SV*     (*named_buff_NEXTKEY) (pTHX_ REGEXP * const rx,
++                                   SV * const lastkey, const U32 flags);
++    SV*     (*named_buff_SCALAR) (pTHX_ REGEXP * const rx,
++                                  const U32 flags);
+     SV*     (*qr_package)(pTHX_ REGEXP * const rx);
+ #ifdef USE_ITHREADS
+     void*   (*dupe) (pTHX_ REGEXP * const rx, CLONE_PARAMS *param);
+ #endif
+ } regexp_engine;
++/*
++  These are passed to the numbered capture variable callbacks as the
++  paren name. >= 1 is reserved for actual numbered captures, i.e. $1,
++  $2 etc.
++*/
++
++#define RXrf_PREMATCH  -2 /* $` / ${^PREMATCH}  */
++#define RXrf_POSTMATCH -1 /* $' / ${^POSTMATCH} */
++#define RXrf_MATCH      0 /* $& / ${^MATCH}     */
++
++#define RXf_HASH_ONE      0x00000001 /* %+ */
++#define RXf_HASH_ALL      0x00000002 /* %- */
++#define RXf_HASH_COUNT    0x00000004 /* scalar %+ / scalar %- */
++
+ /* Flags stored in regexp->extflags 
+  * These are used by code external to the regexp engine
+  *
+diff --git a/t/TEST b/t/TEST
+old mode 100644
+new mode 100755
+diff --git a/t/op/readdir.t b/t/op/readdir.t
+index c4d5ed2..971a02a 100644
+--- a/t/op/readdir.t
++++ b/t/op/readdir.t
+@@ -24,7 +24,7 @@ closedir(OP);
+ ## This range will have to adjust as the number of tests expands,
+ ## as it's counting the number of .t files in src/t
+ ##
+-my ($min, $max) = (140, 160);
++my ($min, $max) = (150, 170);
+ if (@D > $min && @D < $max) { print "ok 2\n"; }
+ else {
+     printf "not ok 2 # counting op/*.t, expect $min < %d < $max files\n",
+diff --git a/t/op/regexp_namedcapture_tie.t b/t/op/regexp_namedcapture_tie.t
+new file mode 100644
+index 0000000..f72970e
+--- /dev/null
++++ b/t/op/regexp_namedcapture_tie.t
+@@ -0,0 +1,48 @@
++#!./perl
++
++BEGIN {
++    chdir 't' if -d 't';
++    @INC = '../lib';
++    require './test.pl';
++}
++
++# Do a basic test on all the tied methods of Tie::Hash::NamedCapture
++
++print "1..12\n";
++
++"hlagh" =~ /
++    (?<a>.)
++    (?<b>.)
++    (?<a>.)
++    .*
++    (?<e>$)
++/x;
++
++# FETCH
++is($+{a}, "h", "FETCH");
++is($+{b}, "l", "FETCH");
++is($-{a}[0], "h", "FETCH");
++is($-{a}[1], "a", "FETCH");
++
++# STORE
++eval { $+{a} = "yon" };
++ok(index($@, "read-only") != -1, "STORE");
++
++# DELETE
++eval { delete $+{a} };
++ok(index($@, "read-only") != -1, "DELETE");
++
++# CLEAR
++eval { %+ = () };
++ok(index($@, "read-only") != -1, "CLEAR");
++
++# EXISTS
++ok(exists $+{e}, "EXISTS");
++ok(!exists $+{d}, "EXISTS");
++
++# FIRSTKEY/NEXTKEY
++is(join('|', sort keys %+), "a|b|e", "FIRSTKEY/NEXTKEY");
++
++# SCALAR
++is(scalar(%+), 3, "SCALAR");
++is(scalar(%-), 3, "SCALAR");
+diff --git a/universal.c b/universal.c
+index 396dd3d..f506441 100644
+--- a/universal.c
++++ b/universal.c
+@@ -16,6 +16,11 @@
+ /* This file contains the code that implements the functions in Perl's
+  * UNIVERSAL package, such as UNIVERSAL->can().
++ *
++ * It is also used to store XS functions that need to be present in
++ * miniperl for a lack of a better place to put them. It might be
++ * clever to move them to seperate XS files which would then be pulled
++ * in by some to-be-written build process.
+  */
+ #include "EXTERN.h"
+@@ -226,11 +231,17 @@ XS(XS_Internals_rehash_seed);
+ XS(XS_Internals_HvREHASH);
+ XS(XS_Internals_inc_sub_generation);
+ XS(XS_re_is_regexp); 
+-XS(XS_re_regname); 
+-XS(XS_re_regnames); 
+-XS(XS_re_regnames_iterinit);
+-XS(XS_re_regnames_iternext);
++XS(XS_re_regname);
++XS(XS_re_regnames);
+ XS(XS_re_regnames_count);
++XS(XS_Tie_Hash_NamedCapture_FETCH);
++XS(XS_Tie_Hash_NamedCapture_STORE);
++XS(XS_Tie_Hash_NamedCapture_DELETE);
++XS(XS_Tie_Hash_NamedCapture_CLEAR);
++XS(XS_Tie_Hash_NamedCapture_EXISTS);
++XS(XS_Tie_Hash_NamedCapture_FIRSTKEY);
++XS(XS_Tie_Hash_NamedCapture_NEXTKEY);
++XS(XS_Tie_Hash_NamedCapture_SCALAR);
+ void
+ Perl_boot_core_UNIVERSAL(pTHX)
+@@ -284,9 +295,15 @@ Perl_boot_core_UNIVERSAL(pTHX)
+     newXSproto("re::is_regexp", XS_re_is_regexp, file, "$");
+     newXSproto("re::regname", XS_re_regname, file, ";$$");
+     newXSproto("re::regnames", XS_re_regnames, file, ";$");
+-    newXSproto("re::regnames_iterinit", XS_re_regnames_iterinit, file, "");
+-    newXSproto("re::regnames_iternext", XS_re_regnames_iternext, file, ";$");
+     newXSproto("re::regnames_count", XS_re_regnames_count, file, "");
++    newXS("Tie::Hash::NamedCapture::FETCH", XS_Tie_Hash_NamedCapture_FETCH, file);
++    newXS("Tie::Hash::NamedCapture::STORE", XS_Tie_Hash_NamedCapture_STORE, file);
++    newXS("Tie::Hash::NamedCapture::DELETE", XS_Tie_Hash_NamedCapture_DELETE, file);
++    newXS("Tie::Hash::NamedCapture::CLEAR", XS_Tie_Hash_NamedCapture_CLEAR, file);
++    newXS("Tie::Hash::NamedCapture::EXISTS", XS_Tie_Hash_NamedCapture_EXISTS, file);
++    newXS("Tie::Hash::NamedCapture::FIRSTKEY", XS_Tie_Hash_NamedCapture_FIRSTKEY, file);
++    newXS("Tie::Hash::NamedCapture::NEXTKEY", XS_Tie_Hash_NamedCapture_NEXTKEY, file);
++    newXS("Tie::Hash::NamedCapture::SCALAR", XS_Tie_Hash_NamedCapture_SCALAR, file);
+ }
+@@ -1072,206 +1089,341 @@ XS(XS_re_is_regexp)
+         /* NOTREACHED */        
+       PUTBACK;
+       return;
++
+     }
+ }
+-XS(XS_re_regname)
++XS(XS_re_regnames_count)
+ {
+-
++    REGEXP *rx = PL_curpm ? PM_GETRE(PL_curpm) : NULL;
++    SV *ret;
+     dVAR; 
+     dXSARGS;
++
++    if (items != 0)
++       Perl_croak(aTHX_ "Usage: %s(%s)", "re::regnames_count", "");
++    PERL_UNUSED_VAR(cv); /* -W */
++    PERL_UNUSED_VAR(ax); /* -Wall */
++    SP -= items;
++
++    if (!rx)
++        XSRETURN_UNDEF;
++
++    ret = CALLREG_NAMEDBUF_SCALAR(rx, RXf_HASH_COUNT);
++
++    SPAGAIN;
++
++    if (ret) {
++        XPUSHs(ret);
++        PUTBACK;
++        return;
++    } else {
++        XSRETURN_UNDEF;
++    }
++}
++
++XS(XS_re_regname)
++{
++    dVAR;
++    dXSARGS;
+     if (items < 1 || items > 2)
+-       Perl_croak(aTHX_ "Usage: %s(%s)", "re::regname", "name[, all ]");
++        Perl_croak(aTHX_ "Usage: %s(%s)", "re::regname", "name[, all ]");
+     PERL_UNUSED_VAR(cv); /* -W */
+     PERL_UNUSED_VAR(ax); /* -Wall */
+     SP -= items;
+-    {
+-      SV *    sv = ST(0);
+-      SV *    all;
+-        regexp *re = PL_curpm ? PM_GETRE(PL_curpm) : NULL;
+-        SV *bufs = NULL;
++    REGEXP * rx = PL_curpm ? PM_GETRE(PL_curpm) : NULL;
++    U32 flags;
++    SV * ret;
+-      if (items < 2)
+-          all = NULL;
+-      else {
+-          all = ST(1);
+-      }
+-        {
+-            if (SvPOK(sv) && re && re->paren_names) {
+-                bufs = CALLREG_NAMEDBUF_FETCH(re,sv,all && SvTRUE(all));
+-                if (bufs) {
+-                    if (all && SvTRUE(all))
+-                        XPUSHs(newRV(bufs));
+-                    else
+-                        XPUSHs(SvREFCNT_inc(bufs));
+-                    XSRETURN(1);
+-                }
+-            }
+-            XSRETURN_UNDEF;
+-        }
+-      PUTBACK;
+-      return;
++    if (!rx)
++        XSRETURN_UNDEF;
++
++    if (items == 2 && SvTRUE(ST(1))) {
++        flags = RXf_HASH_ALL;
++    } else {
++        flags = RXf_HASH_ONE;
++    }
++    ret = CALLREG_NAMEDBUF_FETCH(rx, ST(0), flags);
++
++    if (ret) {
++        if (SvROK(ret))
++            XPUSHs(ret);
++        else
++            XPUSHs(SvREFCNT_inc(ret));
++        XSRETURN(1);
+     }
++    XSRETURN_UNDEF;    
+ }
++
+ XS(XS_re_regnames)
+ {
+-    dVAR; 
++    dVAR;
+     dXSARGS;
+     if (items < 0 || items > 1)
+-       Perl_croak(aTHX_ "Usage: %s(%s)", "re::regnames", "[all]");
++        Perl_croak(aTHX_ "Usage: %s(%s)", "re::regnames", "[all]");
+     PERL_UNUSED_VAR(cv); /* -W */
+     PERL_UNUSED_VAR(ax); /* -Wall */
+     SP -= items;
+-    {
+-      SV *    all;
+-        regexp *re = PL_curpm ? PM_GETRE(PL_curpm) : NULL;
+-        IV count = 0;
++    REGEXP * rx = PL_curpm ? PM_GETRE(PL_curpm) : NULL;
++    U32 flags;
++    IV count = 0;
+-      if (items < 1)
+-          all = NULL;
+-      else {
+-          all = ST(0);
+-      }
+-        {
+-            if (re && re->paren_names) {
+-                HV *hv= re->paren_names;
+-                (void)hv_iterinit(hv);
+-                while (1) {
+-                    HE *temphe = hv_iternext_flags(hv,0);
+-                    if (temphe) {
+-                        IV i;
+-                        IV parno = 0;
+-                        SV* sv_dat = HeVAL(temphe);
+-                        I32 *nums = (I32*)SvPVX(sv_dat);
+-                        for ( i = 0; i < SvIVX(sv_dat); i++ ) {
+-                            if ((I32)(re->lastcloseparen) >= nums[i] &&
+-                                re->offs[nums[i]].start != -1 &&
+-                                re->offs[nums[i]].end != -1)
+-                            {
+-                                parno = nums[i];
+-                                break;
+-                            }
+-                        }
+-                        if (parno || (all && SvTRUE(all))) {
+-                            STRLEN len;
+-                            char *pv = HePV(temphe, len);
+-                            if ( GIMME_V == G_ARRAY ) 
+-                                XPUSHs(newSVpvn(pv,len));
+-                            count++;
+-                        }
+-                    } else {
++    if (!rx)
++        XSRETURN_UNDEF;
++
++    if (items == 1 && SvTRUE(ST(0))) {
++        flags = 1;
++    } else {
++        flags = 0;
++    }
++
++    if (rx && rx->paren_names) {
++        HV *hv= rx->paren_names;
++        (void)hv_iterinit(hv);
++        while (1) {
++            HE *temphe = hv_iternext_flags(hv,0);
++            if (temphe) {
++                IV i;
++                IV parno = 0;
++                SV* sv_dat = HeVAL(temphe);
++                I32 *nums = (I32*)SvPVX(sv_dat);
++                for ( i = 0; i < SvIVX(sv_dat); i++ ) {
++                    if ((I32)(rx->lastcloseparen) >= nums[i] &&
++                        rx->offs[nums[i]].start != -1 &&
++                        rx->offs[nums[i]].end != -1)
++                    {
++                        parno = nums[i];
+                         break;
+                     }
+                 }
++                if (parno || flags) {
++                    STRLEN len;
++                    char *pv = HePV(temphe, len);
++                    if ( GIMME_V == G_ARRAY ) 
++                        XPUSHs(newSVpvn(pv,len));
++                    count++;
++                }
++            } else {
++                break;
+             }
+-            if ( GIMME_V == G_ARRAY ) 
+-                XSRETURN(count);
+-            else 
+-                XSRETURN_UNDEF;
+-        }    
+-      PUTBACK;
+-      return;
++        }
+     }
++    
++    if ( GIMME_V == G_ARRAY )
++        XSRETURN(count);
++    else 
++        XSRETURN_UNDEF;
+ }
+-
+-XS(XS_re_regnames_iterinit)
++XS(XS_Tie_Hash_NamedCapture_FETCH)
+ {
+-    dVAR; 
++    dVAR;
+     dXSARGS;
+-    if (items != 0)
+-      Perl_croak(aTHX_ "Usage: re::regnames_iterinit()");
+-    PERL_UNUSED_VAR(cv); /* -W */
+-    PERL_UNUSED_VAR(ax); /* -Wall */
++    REGEXP * rx = PL_curpm ? PM_GETRE(PL_curpm) : NULL;
++    U32 flags;
++    SV * ret;
++
++    if (items != 2)
++        Perl_croak(aTHX_ "Usage: Tie::Hash::NamedCapture::STORE($key, $flags)");
++
++    if (!rx)
++        XSRETURN_UNDEF;
++
+     SP -= items;
+-    {
+-        regexp *re = PL_curpm ? PM_GETRE(PL_curpm) : NULL;
+-        if (re && re->paren_names) {
+-            (void)hv_iterinit(re->paren_names);
+-            XPUSHs(newSViv(HvTOTALKEYS(re->paren_names)));
+-        } else {
++
++    flags = (U32)INT2PTR(IV,SvIV(SvRV((SV*)ST(0))));
++    ret = CALLREG_NAMEDBUF_FETCH(rx, ST(1), flags);
++
++    SPAGAIN;
++
++    if (ret) {
++        if (SvROK(ret))
++            XPUSHs(ret);
++        else
++            XPUSHs(SvREFCNT_inc(ret));
++        PUTBACK;
++        return;
++    }
++    XSRETURN_UNDEF;
++}
++
++XS(XS_Tie_Hash_NamedCapture_STORE)
++{
++    dVAR;
++    dXSARGS;
++    REGEXP * rx = PL_curpm ? PM_GETRE(PL_curpm) : NULL;
++    U32 flags;
++
++    if (items != 3)
++        Perl_croak(aTHX_ "Usage: Tie::Hash::NamedCapture::STORE($key, $value, $flags)");
++
++    if (!rx) {
++        if (!PL_localizing)
++            Perl_croak(aTHX_ PL_no_modify);
++        else
+             XSRETURN_UNDEF;
+-        }  
+-      PUTBACK;
+-      return;
+     }
++
++    SP -= items;
++
++    flags = (U32)INT2PTR(IV,SvIV(SvRV((SV*)ST(0))));
++    CALLREG_NAMEDBUF_STORE(rx,ST(1), ST(2), flags);
+ }
++XS(XS_Tie_Hash_NamedCapture_DELETE)
++{
++    dVAR;
++    dXSARGS;
++    REGEXP * rx = PL_curpm ? PM_GETRE(PL_curpm) : NULL;
++    U32 flags;
+-XS(XS_re_regnames_iternext)
++    if (items != 2)
++        Perl_croak(aTHX_ "Usage: Tie::Hash::NamedCapture::DELETE($key, $flags)");
++
++    if (!rx)
++        Perl_croak(aTHX_ PL_no_modify);
++
++    SP -= items;
++
++    flags = (U32)INT2PTR(IV,SvIV(SvRV((SV*)ST(0))));
++    CALLREG_NAMEDBUF_DELETE(rx, ST(1), flags);
++}
++
++XS(XS_Tie_Hash_NamedCapture_CLEAR)
+ {
+-    dVAR; 
++    dVAR;
+     dXSARGS;
+-    if (items < 0 || items > 1)
+-       Perl_croak(aTHX_ "Usage: %s(%s)", "re::regnames_iternext", "[all]");
+-    PERL_UNUSED_VAR(cv); /* -W */
+-    PERL_UNUSED_VAR(ax); /* -Wall */
++    REGEXP * rx = PL_curpm ? PM_GETRE(PL_curpm) : NULL;
++    U32 flags;
++
++    if (items != 1)
++        Perl_croak(aTHX_ "Usage: Tie::Hash::NamedCapture::CLEAR($flags)");
++
++    if (!rx)
++        Perl_croak(aTHX_ PL_no_modify);
++
+     SP -= items;
+-    {
+-      SV *    all;
+-        regexp *re = PL_curpm ? PM_GETRE(PL_curpm) : NULL;
+-      if (items < 1)
+-          all = NULL;
+-      else {
+-          all = ST(0);
+-      }
+-        if (re && re->paren_names) {
+-            HV *hv= re->paren_names;
+-            while (1) {
+-                HE *temphe = hv_iternext_flags(hv,0);
+-                if (temphe) {
+-                    IV i;
+-                    IV parno = 0;
+-                    SV* sv_dat = HeVAL(temphe);
+-                    I32 *nums = (I32*)SvPVX(sv_dat);
+-                    for ( i = 0; i < SvIVX(sv_dat); i++ ) {
+-                        if ((I32)(re->lastcloseparen) >= nums[i] &&
+-                            re->offs[nums[i]].start != -1 &&
+-                            re->offs[nums[i]].end != -1)
+-                        {
+-                            parno = nums[i];
+-                            break;
+-                        }
+-                    }
+-                    if (parno || (all && SvTRUE(all))) {
+-                        STRLEN len;
+-                        char *pv = HePV(temphe, len);
+-                        XPUSHs(newSVpvn(pv,len));
+-                        XSRETURN(1);    
+-                    }
+-                } else {
+-                    break;
+-                }
+-            }
+-        }
++    flags = (U32)INT2PTR(IV,SvIV(SvRV((SV*)ST(0))));
++    CALLREG_NAMEDBUF_CLEAR(rx, flags);
++}
++
++XS(XS_Tie_Hash_NamedCapture_EXISTS)
++{
++    dVAR;
++    dXSARGS;
++    REGEXP * rx = PL_curpm ? PM_GETRE(PL_curpm) : NULL;
++    U32 flags;
++    bool exists;
++
++    if (items != 2)
++        Perl_croak(aTHX_ "Usage: Tie::Hash::NamedCapture::EXISTS($key, $flags)");
++
++    if (!rx)
+         XSRETURN_UNDEF;
+-      PUTBACK;
+-      return;
++
++    SP -= items;
++
++    flags = (U32)INT2PTR(IV,SvIV(SvRV((SV*)ST(0))));
++    exists = CALLREG_NAMEDBUF_EXISTS(rx, ST(1), flags);
++
++    SPAGAIN;
++
++    if (exists) {
++        XSRETURN_YES;
++    } else {
++        XSRETURN_NO;
+     }
+ }
++XS(XS_Tie_Hash_NamedCapture_FIRSTKEY)
++{
++    dVAR;
++    dXSARGS;
++    REGEXP * rx = PL_curpm ? PM_GETRE(PL_curpm) : NULL;
++    U32 flags;
++    SV * ret;
+-XS(XS_re_regnames_count)
++    if (items != 1)
++        Perl_croak(aTHX_ "Usage: Tie::Hash::NamedCapture::FIRSTKEY()");
++
++    if (!rx)
++        XSRETURN_UNDEF;
++
++    SP -= items;
++
++    flags = (U32)INT2PTR(IV,SvIV(SvRV((SV*)ST(0))));
++    ret = CALLREG_NAMEDBUF_FIRSTKEY(rx, flags);
++
++    SPAGAIN;
++
++    if (ret) {
++        XPUSHs(SvREFCNT_inc(ret));
++        PUTBACK;
++    } else {
++        XSRETURN_UNDEF;
++    }
++
++}
++
++XS(XS_Tie_Hash_NamedCapture_NEXTKEY)
+ {
+-    regexp *re = PL_curpm ? PM_GETRE(PL_curpm) : NULL;
+-    dVAR; 
++    dVAR;
+     dXSARGS;
++    REGEXP * rx = PL_curpm ? PM_GETRE(PL_curpm) : NULL;
++    U32 flags;
++    SV * ret;
++
++    if (items != 2)
++        Perl_croak(aTHX_ "Usage: Tie::Hash::NamedCapture::NEXTKEY($lastkey)");
++
++    if (!rx)
++        XSRETURN_UNDEF;
+-    if (items != 0)
+-       Perl_croak(aTHX_ "Usage: %s(%s)", "re::regnames_count", "");
+-    PERL_UNUSED_VAR(cv); /* -W */
+-    PERL_UNUSED_VAR(ax); /* -Wall */
+     SP -= items;
+-    
+-    if (re && re->paren_names) {
+-        XPUSHs(newSViv(HvTOTALKEYS(re->paren_names)));
++
++    flags = (U32)INT2PTR(IV,SvIV(SvRV((SV*)ST(0))));
++    ret = CALLREG_NAMEDBUF_NEXTKEY(rx, ST(1), flags);
++
++    SPAGAIN;
++
++    if (ret) {
++        XPUSHs(ret);
+     } else {
+         XSRETURN_UNDEF;
+     }  
+     PUTBACK;
+-    return;
++}
++
++XS(XS_Tie_Hash_NamedCapture_SCALAR)
++{
++    dVAR;
++    dXSARGS;
++    REGEXP * rx = PL_curpm ? PM_GETRE(PL_curpm) : NULL;
++    U32 flags;
++    SV * ret;
++
++    if (items != 1)
++        Perl_croak(aTHX_ "Usage: Tie::Hash::NamedCapture::SCALAR()");
++
++    if (!rx)
++        XSRETURN_UNDEF;
++
++    SP -= items;
++
++    flags = (U32)INT2PTR(IV,SvIV(SvRV((SV*)ST(0))));
++    ret = CALLREG_NAMEDBUF_SCALAR(rx, flags);
++
++    SPAGAIN;
++
++    if (ret) {
++        XPUSHs(ret);
++        PUTBACK;
++        return;
++    } else {
++        XSRETURN_UNDEF;
++    }
+ }
index b719753e5daefa37211574444139daf4abed0610..34895e21b6aa348499b256366d95b9815dcd6f35 100644 (file)
@@ -5,7 +5,7 @@ Test the C<stash> method
 =cut
 
 use strict;
-use Test::More tests => 4;
+use Test::More tests => 5;
 
 use re::engine::Plugin (
     comp => sub {
@@ -19,6 +19,9 @@ use re::engine::Plugin (
         my ($re, $str) = @_;
 
         my $stash = $re->stash;
+        my $ret = $re->stash( $stash );
+        ok(!$ret, "stash returns no value on assignment");
+
         my %h = qw( 0 a 1 o 2 e 3 u );
         for (keys %h) {
             is($h{$_}, $stash->[$_]);
diff --git a/t/methods/str/modify.t b/t/methods/str/modify.t
new file mode 100644 (file)
index 0000000..a30d76a
--- /dev/null
@@ -0,0 +1,17 @@
+use strict;
+use Test::More tests => 1;
+use re::engine::Plugin (
+    exec => sub {
+        my ($re, $str) = @_;
+
+        $$str = "eek";
+
+        return 1;
+    },
+);
+
+my $sv = "ook";
+if (\$sv =~ /pattern/) {
+    is($sv, "eek");
+}
+
index f79ba36349c7f4de9fa36c914c61b643a3368b9b..ed45fb1b774c6879752d60078ce55ef4311f6c93 100644 (file)
@@ -4,18 +4,18 @@ use re::engine::Plugin (
     exec => sub {
         my ($re, $str) = @_;
 
-        is_deeply($str, $re->str);
+        isa_ok($str, $re->pattern);
 
         return 1;
     },
 );
 
 my $sv;
-"SCALAR" =~ \$sv;
-"REF"    =~ \\$sv;
-"ARRAY"  =~ [];
-"HASH"   =~ {};
-"GLOB"   =~ \*STDIN;
-"CODE"   =~ sub {};
-"main"   =~ bless {} => "main";
+\$sv    =~ "SCALAR";
+\\$sv   =~ "REF";
+[]      =~ "ARRAY";
+{}      =~ "HASH";
+\*STDIN =~ "GLOB";
+sub {}  =~ "CODE";
+bless({} => "main") =~ "main"
 
diff --git a/t/named_buff/CLEAR.t b/t/named_buff/CLEAR.t
new file mode 100644 (file)
index 0000000..fcd97a7
--- /dev/null
@@ -0,0 +1,38 @@
+use strict;
+use Test::More tests => 6;
+
+use re::engine::Plugin (
+    exec => sub {
+        my $re = shift;
+
+        $re->stash( [
+            { flags => 0 },
+            { flags => 0 },
+            { flags => 0 },
+            { flags => 1 },
+            { flags => 1 },
+            { flags => 1 },
+        ] );
+
+        $re->named_captures(
+            CLEAR => sub {
+                my ($re, $flags) = @_;
+                my $hv = shift @{ $re->stash };
+
+                is($flags, $hv->{flags}, "flags == $flags");
+            },
+        );
+
+        1;
+    },
+);
+
+"a" =~ /a/;
+%+ = ();
+%+ = (a => 1);
+undef %+;
+%- = ();
+%- = (b => 1);
+undef %-;
+
+
diff --git a/t/named_buff/DELETE.t b/t/named_buff/DELETE.t
new file mode 100644 (file)
index 0000000..5eee569
--- /dev/null
@@ -0,0 +1,37 @@
+use strict;
+use Test::More tests => 4;
+
+use re::engine::Plugin (
+    exec => sub {
+        my $re = shift;
+
+        $re->stash( [
+            {
+                key => 'one',
+                flags => 0,
+            },
+            {
+                key => 'two',
+                flags => 1,
+            },
+        ] );
+
+        $re->named_captures(
+            DELETE => sub {
+                my ($re, $key, $flags) = @_;
+                my $hv = shift @{ $re->stash };
+
+                is($key, $hv->{key}, "key eq $key");
+                is($flags, $hv->{flags}, "flags == $flags");
+            },
+        );
+
+        1;
+    },
+);
+
+"a" =~ /a/;
+delete $+{one};
+delete $-{two};
+
+
diff --git a/t/named_buff/EXISTS.t b/t/named_buff/EXISTS.t
new file mode 100644 (file)
index 0000000..7b5a7dc
--- /dev/null
@@ -0,0 +1,31 @@
+use strict;
+use Test::More tests => 6;
+
+use re::engine::Plugin (
+    exec => sub {
+        my $re = shift;
+
+        $re->stash( [
+            { key => "boob", flags => 0, ret => 1 },
+            { key => "ies",  flags => 1, ret => 0 },
+        ] );
+
+        $re->named_captures(
+            EXISTS => sub {
+                my ($re, $key, $flags) = @_;
+                my $hv = shift @{ $re->stash };
+
+                is($key, $hv->{key}, "key == $key");
+                is($flags, $hv->{flags}, "flags == $flags");
+                return $hv->{ret};
+            },
+        );
+
+        1;
+    },
+);
+
+"a" =~ /a/;
+ok(exists $+{boob});
+ok(!exists $-{ies});;
+
diff --git a/t/named_buff/FETCH.t b/t/named_buff/FETCH.t
new file mode 100644 (file)
index 0000000..b8e88a9
--- /dev/null
@@ -0,0 +1,51 @@
+use strict;
+use Test::More tests => 16;
+
+use re::engine::Plugin (
+    exec => sub {
+        my $re = shift;
+
+        $re->stash( [
+            {
+                key => "a",
+                flags => 0,
+                ret => "b",
+            },
+            {
+                key => "c",
+                flags => 0,
+                ret => "d",
+            },
+            {
+                key => "e",
+                flags => 1,
+                ret => "f",
+            },
+            {
+                key => "g",
+                flags => 1,
+                ret => \%ENV,
+            },
+        ] );
+
+        $re->named_captures(
+            FETCH => sub {
+                my ($re, $key, $flags) = @_;
+                my $hv = shift @{ $re->stash };
+
+                is($key, $hv->{key}, "key == $key");
+                is($flags, $hv->{flags}, "flags == $flags");
+                is($hv->{ret}, $hv->{ret}, "ret = $hv->{ret}");
+                return $hv->{ret};
+            },
+        );
+
+        1;
+    },
+);
+
+"a" =~ /a/;
+cmp_ok($+{a}, 'eq', "b");
+cmp_ok($+{c}, 'eq', "d");
+cmp_ok($-{e}, 'eq', "f");
+cmp_ok($-{g}, '==', \%ENV);
diff --git a/t/named_buff/FIRSTKEY.t b/t/named_buff/FIRSTKEY.t
new file mode 100644 (file)
index 0000000..b02ca02
--- /dev/null
@@ -0,0 +1,53 @@
+use strict;
+use Test::More tests => 10;
+
+use re::engine::Plugin (
+    exec => sub {
+        my $re = shift;
+
+        my $stash = 0;
+        my @stash = (
+            {
+                key => "a",
+                flags => 0,
+                ret => "b",
+            },
+            {
+                key => "c",
+                flags => 1,
+                ret => "d",
+            },
+        );
+
+        $re->named_captures(
+            FIRSTKEY => sub {
+                my ($re, $flags) = @_;
+                my $hv = $stash[$stash];
+
+                return $hv->{key};
+            },
+            FETCH => sub {
+                my ($re, $key, $flags) = @_;
+                my $hv = $stash[$stash++];
+
+                is($key, $hv->{key}, "key == $key");
+                is($flags, $hv->{flags}, "flags == $flags");
+                is($hv->{ret}, $hv->{ret}, "ret = $hv->{ret}");
+                return $hv->{ret};
+            },
+        );
+
+        1;
+    },
+);
+
+"a" =~ /a/;
+my ($k, $v);
+
+($k, $v) = each %+;
+is($k, "a");
+is($v, "b");
+
+($k, $v) = each %-;
+is($k, "c");
+is($v, "d");
diff --git a/t/named_buff/NEXTKEY.t b/t/named_buff/NEXTKEY.t
new file mode 100644 (file)
index 0000000..74a8d29
--- /dev/null
@@ -0,0 +1,29 @@
+use strict;
+use Test::More tests => 6;
+
+use re::engine::Plugin (
+    exec => sub {
+        my $re = shift;
+
+        my @keys = ("a" .. "f");
+
+        $re->named_captures(
+            FIRSTKEY => sub { shift @keys },
+            NEXTKEY  => sub {
+                my ($re, $lastkey, $flag) = @_;
+                my $key = shift @keys;
+
+                is(chr(ord($key)-1), $lastkey, "$lastkey value makes sense")
+                    if defined $key;
+
+                return $key;
+            },
+        );
+
+        1;
+    },
+);
+
+"a" =~ /a/;
+my $key = join "|", keys %+;
+is($key, "a|b|c|d|e|f", "key row correct");
diff --git a/t/named_buff/SCALAR.t b/t/named_buff/SCALAR.t
new file mode 100644 (file)
index 0000000..cb5e3f3
--- /dev/null
@@ -0,0 +1,31 @@
+use strict;
+use Test::More tests => 6;
+
+use re::engine::Plugin (
+    exec => sub {
+        my ($re) = @_;
+
+        my @stash = (
+            { flags => 0, ret => "ook" },
+            { flags => 1, ret => "eek" },
+        );
+
+        $re->named_captures(
+            SCALAR => sub {
+                my ($re, $flags) = @_;
+                my $hv = shift @stash;
+
+                is($flags, $hv->{flags}, "flags == $flags");
+                ok($hv->{ret}, "ret == $hv->{ret}");
+
+                return $hv->{ret};
+            },
+        );
+
+        1;
+    },
+);
+
+"a" =~ /a/;
+is(scalar %+, "ook");
+is(scalar %-, "eek");
diff --git a/t/named_buff/STORE.t b/t/named_buff/STORE.t
new file mode 100644 (file)
index 0000000..2ed6b4f
--- /dev/null
@@ -0,0 +1,52 @@
+use strict;
+use Test::More tests => 12;
+
+use re::engine::Plugin (
+    exec => sub {
+        my $re = shift;
+
+        $re->stash( [
+            {
+                key => 'one',
+                value => 'a',
+                flags => 0,
+            },
+            {
+                key => 'two',
+                value => 'b',
+                flags => 0,
+            },
+            {
+                key => 'three',
+                value => 'c',
+                flags => 1,
+            },
+            {
+                key => 'four',
+                value => 'd',
+                flags => 1,
+            },
+        ] );
+
+        $re->named_captures(
+            STORE => sub {
+                my ($re, $key, $value, $flags) = @_;
+                my $hv = shift @{ $re->stash };
+
+                is($key, $hv->{key}, "key eq $key");
+                is($value, $hv->{value}, "value eq $value");
+                is($flags, $hv->{flags}, "flags == $flags");
+            },
+        );
+
+        1;
+    },
+);
+
+"a" =~ /a/;
+$+{one}   = "a";
+$+{two}   = "b";
+$-{three} = "c";
+$-{four}  = "d";
+
+
diff --git a/t/usage/basic.pm b/t/usage/basic.pm
new file mode 100644 (file)
index 0000000..80add8b
--- /dev/null
@@ -0,0 +1,31 @@
+package basic;
+use strict;
+
+# Note the (), doesn't call ->import
+use re::engine::Plugin ();
+
+sub import {
+    # Populates %^H with re::engine::Plugin hooks
+    re::engine::Plugin->import(
+        exec => \&exec,
+    );
+}
+
+*unimport = \&re::engine::Plugin::unimport;
+
+sub exec
+{
+    my ($re, $str) = @_;
+
+    $re->num_captures(
+        FETCH => sub {
+            my ($re, $paren) = @_;
+
+            $str . "_" . $paren;
+        }
+    );
+
+    1;
+}
+
+1;
diff --git a/t/usage/basic.t b/t/usage/basic.t
new file mode 100644 (file)
index 0000000..d7f2c57
--- /dev/null
@@ -0,0 +1,9 @@
+use strict;
+use lib 't/usage';
+use Test::More tests => 1;
+
+use basic;
+
+"str" =~ /pattern/;
+
+is($1, "str_1");
diff --git a/t/usage/import.pm b/t/usage/import.pm
new file mode 100644 (file)
index 0000000..0c364ec
--- /dev/null
@@ -0,0 +1,22 @@
+package import;
+use strict;
+
+sub exec;
+use re::engine::Plugin ':import';
+
+sub exec
+{
+    my ($re, $str) = @_;
+
+    $re->num_captures(
+        FETCH => sub {
+            my ($re, $paren) = @_;
+
+            $str . "_" . $paren;
+        }
+    );
+
+    1;
+}
+
+1;
diff --git a/t/usage/import.t b/t/usage/import.t
new file mode 100644 (file)
index 0000000..40aad43
--- /dev/null
@@ -0,0 +1,40 @@
+use strict;
+use lib 't/usage';
+use Test::More skip_all => "Needs lameness in user code";
+
+#    my $caller = caller;
+#
+# This won't work unless the subs are predeclared before the C<use> statement
+#    # Handle import tags
+#    if (@_ == 1) {
+#        if ($_[0] ne ":import") {
+#            require Carp;
+#            Carp::croak("Unknown tag '$_[0]'");
+#        }
+#
+#        # We have :import, generate import and unimport methods in the
+#        # calling package
+#        my %pkg;
+#        for (qw(comp exec)) {
+#            no strict 'refs';
+#            $pkg{$_} = *{"$caller\::$_"}{CODE} if *{"$caller\::$_"}{CODE};
+#        }
+#
+#        use Data::Dumper;
+#        warn Dumper \%pkg;
+#
+#        no strict 'refs';
+#        *{"$caller\::import"} = sub {
+#            __PACKAGE__->import(%pkg);
+#        };
+#        *{"$caller\::unimport"} = \&unimport;
+#
+#        return;
+#    }
+#
+
+use import;
+
+"ook" =~ /pattern/;
+
+is($1, "ook_1");