This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
$+ and $^N not always correct on backtracking
authorDavid Mitchell <davem@iabyn.com>
Fri, 25 May 2012 14:03:29 +0000 (15:03 +0100)
committerDavid Mitchell <davem@iabyn.com>
Wed, 13 Jun 2012 12:32:54 +0000 (13:32 +0100)
Certain ops (TRIE, BRANCH, CURLYM, CURLY and related) didn't always
correctly restore or update lastcloseparen ($^N) - and sometimes lastparen
($+) - when backtracking, or doing a zero-length match (i.e. A* matching
zero times).

Fix this by saving lastcloseparen and lastparen in the relevant
regmatch_state union sub structs, then restoring as necessary.

regexec.c
regexp.h
t/re/re_tests

index 1547c70..9de68a7 100644 (file)
--- a/regexec.c
+++ b/regexec.c
@@ -3491,6 +3491,7 @@ S_regmatch(pTHX_ regmatch_info *reginfo, regnode *prog)
                for (n = rex->lastparen; n > ST.lastparen; n--)
                    rex->offs[n].end = -1;
                rex->lastparen = n;
+               rex->lastcloseparen = ST.lastcloseparen;
            }
            if (!--ST.accepted) {
                DEBUG_EXECUTE_r({
@@ -3525,6 +3526,7 @@ S_regmatch(pTHX_ regmatch_info *reginfo, regnode *prog)
 
             if ( ST.jump) {
                 ST.lastparen = rex->lastparen;
+                ST.lastcloseparen = rex->lastcloseparen;
                REGCP_SET(ST.cp);
             }
 
@@ -5010,6 +5012,7 @@ NULL
        case BRANCH:        /*  /(...|A|...)/ */
            scan = NEXTOPER(scan); /* scan now points to inner node */
            ST.lastparen = rex->lastparen;
+           ST.lastcloseparen = rex->lastcloseparen;
            ST.next_branch = next;
            REGCP_SET(ST.cp);
            PL_reginput = locinput;
@@ -5046,7 +5049,7 @@ NULL
            for (n = rex->lastparen; n > ST.lastparen; n--)
                rex->offs[n].end = -1;
            rex->lastparen = n;
-           /*dmq: rex->lastcloseparen = n; */
+           rex->lastcloseparen = ST.lastcloseparen;
            scan = ST.next_branch;
            /* no more branches? */
            if (!scan || (OP(scan) != BRANCH && OP(scan) != BRANCHJ)) {
@@ -5080,13 +5083,14 @@ NULL
            ST.me = scan;
            scan = NEXTOPER(scan) + NODE_STEP_REGNODE;
 
+           ST.lastparen      = rex->lastparen;
+           ST.lastcloseparen = rex->lastcloseparen;
+
            /* if paren positive, emulate an OPEN/CLOSE around A */
            if (ST.me->flags) {
                U32 paren = ST.me->flags;
                if (paren > PL_regsize)
                    PL_regsize = paren;
-               if (paren > rex->lastparen)
-                   rex->lastparen = paren;
                scan += NEXT_OFF(scan); /* Skip former OPEN. */
            }
            ST.A = scan;
@@ -5212,13 +5216,15 @@ NULL
            }
 
            if (ST.me->flags) {
-               /* mark current A as captured */
+               /* emulate CLOSE: mark current A as captured */
                I32 paren = ST.me->flags;
                if (ST.count) {
                    rex->offs[paren].start
                        = HOPc(PL_reginput, -ST.alen) - PL_bostr;
                    rex->offs[paren].end = PL_reginput - PL_bostr;
-                   /*dmq: rex->lastcloseparen = paren; */
+                   if ((U32)paren > rex->lastparen)
+                       rex->lastparen = paren;
+                   rex->lastcloseparen = paren;
                }
                else
                    rex->offs[paren].end = -1;
@@ -5237,6 +5243,8 @@ NULL
 
        case CURLYM_B_fail: /* just failed to match a B */
            REGCP_UNWIND(ST.cp);
+           rex->lastparen      = ST.lastparen;
+           rex->lastcloseparen = ST.lastcloseparen;
            if (ST.minmod) {
                I32 max = ARG2(ST.me);
                if (max != REG_INFTY && ST.count == max)
@@ -5258,10 +5266,15 @@ NULL
        if (success) { \
            rex->offs[paren].start = HOPc(locinput, -1) - PL_bostr; \
            rex->offs[paren].end = locinput - PL_bostr; \
+           if (paren > rex->lastparen) \
+               rex->lastparen = paren; \
            rex->lastcloseparen = paren; \
        } \
-       else \
+       else \
            rex->offs[paren].end = -1; \
+           rex->lastparen      = ST.lastparen; \
+           rex->lastcloseparen = ST.lastcloseparen; \
+       } \
     }
 
        case STAR:              /*  /A*B/ where A is width 1 */
@@ -5278,10 +5291,10 @@ NULL
            goto repeat;
        case CURLYN:            /*  /(A){m,n}B/ where A is width 1 */
            ST.paren = scan->flags;     /* Which paren to set */
+           ST.lastparen      = rex->lastparen;
+           ST.lastcloseparen = rex->lastcloseparen;
            if (ST.paren > PL_regsize)
                PL_regsize = ST.paren;
-           if (ST.paren > rex->lastparen)
-               rex->lastparen = ST.paren;
            ST.min = ARG1(scan);  /* min to match */
            ST.max = ARG2(scan);  /* max to match */
            if (cur_eval && cur_eval->u.eval.close_paren &&
index 2748578..db36edd 100644 (file)
--- a/regexp.h
+++ b/regexp.h
@@ -610,6 +610,7 @@ typedef struct regmatch_state {
            /* this first element must match u.yes */
            struct regmatch_state *prev_yes_state;
            U32 lastparen;
+           U32 lastcloseparen;
            CHECKPOINT cp;
            
         } branchlike;
@@ -618,6 +619,7 @@ typedef struct regmatch_state {
            /* the first elements must match u.branchlike */
            struct regmatch_state *prev_yes_state;
            U32 lastparen;
+           U32 lastcloseparen;
            CHECKPOINT cp;
            
            regnode *next_branch; /* next branch node */
@@ -627,6 +629,7 @@ typedef struct regmatch_state {
            /* the first elements must match u.branchlike */
            struct regmatch_state *prev_yes_state;
            U32 lastparen;
+           U32 lastcloseparen;
            CHECKPOINT cp;
 
            U32         accepted; /* how many accepting states left */
@@ -709,6 +712,8 @@ typedef struct regmatch_state {
            struct regmatch_state *prev_yes_state;
            I32 c1, c2;         /* case fold search */
            CHECKPOINT cp;
+           U32 lastparen;
+           U32 lastcloseparen;
            I32 alen;           /* length of first-matched A string */
            I32 count;
            bool minmod;
@@ -719,6 +724,8 @@ typedef struct regmatch_state {
        struct {
            U32 paren;
            CHECKPOINT cp;
+           U32 lastparen;
+           U32 lastcloseparen;
            I32 c1, c2;         /* case fold search */
            char *maxpos;       /* highest possible point in string to match */
            char *oldloc;       /* the previous locinput */
index 982a5a7..e54c3ab 100644 (file)
@@ -1628,4 +1628,18 @@ ab[c\\\](??{"x"})]{3}d   ab\\](d y       -       -
 /^(?i:foo||baz)bar$/   bazbar  y       <$&>    <bazbar>
 /^(?i:foo||baz)bar$/   foobar  y       <$&>    <foobar>
 
+# $^N, $+ on backtrackracking
+# BRANCH
+^(.)(?:(..)|B)[CX]     ABCDE   y       $^N-$+  A-A     -
+# TRIE
+^(.)(?:BC(.)|B)[CX]    ABCDE   y       $^N-$+  A-A     -
+# CURLYX
+^(.)(?:(.)+)*[BX]      ABCDE   y       $^N-$+  A-A     -
+# CURLYM
+^(.)(BC)*      ABCDE   y       $^N-$+  BC-BC   -
+^(.)(BC)*[BX]  ABCDE   y       $^N-$+  A-A     -
+# CURLYN
+^(.)(B)*.[DX]  ABCDE   y       $^N-$+  B-B     -
+^(.)(B)*.[CX]  ABCDE   y       $^N-$+  A-A     -
+
 # vim: softtabstop=0 noexpandtab