Error in the formulation of the new warning, spotted by Dominic Dunlop
[p5sagit/p5-mst-13.2.git] / regexec.c
index 195ab25..53b9ff7 100644 (file)
--- a/regexec.c
+++ b/regexec.c
@@ -271,23 +271,6 @@ S_regcppop(pTHX_ const regexp *rex)
 
 #define regcpblow(cp) LEAVE_SCOPE(cp)  /* Ignores regcppush()ed data. */
 
-#define TRYPAREN(paren, n, input, where) {                     \
-    if (paren) {                                               \
-       if (n) {                                                \
-           PL_regstartp[paren] = HOPc(input, -1) - PL_bostr;   \
-           PL_regendp[paren] = input - PL_bostr;               \
-       }                                                       \
-       else                                                    \
-           PL_regendp[paren] = -1;                             \
-    }                                                          \
-    REGMATCH(next, where);                                     \
-    if (result)                                                        \
-       sayYES;                                                 \
-    if (paren && n)                                            \
-       PL_regendp[paren] = -1;                                 \
-}
-
-
 /*
  * pregexec and friends
  */
@@ -2604,36 +2587,38 @@ S_push_slab(pTHX)
  */
  
 /* *** every FOO_fail should = FOO+1 */
-#define resume_TRIE1       (REGNODE_MAX+1)
-#define resume_TRIE2       (REGNODE_MAX+2)
-#define EVAL_A             (REGNODE_MAX+3)
-#define EVAL_A_fail        (REGNODE_MAX+4)
-#define resume_CURLYX      (REGNODE_MAX+5)
-#define resume_WHILEM1     (REGNODE_MAX+6)
-#define resume_WHILEM2     (REGNODE_MAX+7)
-#define resume_WHILEM3     (REGNODE_MAX+8)
-#define resume_WHILEM4     (REGNODE_MAX+9)
-#define resume_WHILEM5     (REGNODE_MAX+10)
-#define resume_WHILEM6     (REGNODE_MAX+11)
-#define BRANCH_next        (REGNODE_MAX+12)
-#define BRANCH_next_fail    (REGNODE_MAX+13)
-#define CURLYM_A           (REGNODE_MAX+14)
-#define CURLYM_A_fail      (REGNODE_MAX+15)
-#define CURLYM_B           (REGNODE_MAX+16)
-#define CURLYM_B_fail      (REGNODE_MAX+17)
-#define IFMATCH_A          (REGNODE_MAX+18)
-#define IFMATCH_A_fail     (REGNODE_MAX+19)
-#define resume_PLUS1       (REGNODE_MAX+20)
-#define resume_PLUS2       (REGNODE_MAX+21)
-#define resume_PLUS3       (REGNODE_MAX+22)
-#define resume_PLUS4       (REGNODE_MAX+23)
-
+#define TRIE_next              (REGNODE_MAX+1)
+#define TRIE_next_fail         (REGNODE_MAX+2)
+#define EVAL_A                 (REGNODE_MAX+3)
+#define EVAL_A_fail            (REGNODE_MAX+4)
+#define resume_CURLYX          (REGNODE_MAX+5)
+#define resume_WHILEM1         (REGNODE_MAX+6)
+#define resume_WHILEM2         (REGNODE_MAX+7)
+#define resume_WHILEM3         (REGNODE_MAX+8)
+#define resume_WHILEM4         (REGNODE_MAX+9)
+#define resume_WHILEM5         (REGNODE_MAX+10)
+#define resume_WHILEM6         (REGNODE_MAX+11)
+#define BRANCH_next            (REGNODE_MAX+12)
+#define BRANCH_next_fail       (REGNODE_MAX+13)
+#define CURLYM_A               (REGNODE_MAX+14)
+#define CURLYM_A_fail          (REGNODE_MAX+15)
+#define CURLYM_B               (REGNODE_MAX+16)
+#define CURLYM_B_fail          (REGNODE_MAX+17)
+#define IFMATCH_A              (REGNODE_MAX+18)
+#define IFMATCH_A_fail         (REGNODE_MAX+19)
+#define CURLY_B_min_known      (REGNODE_MAX+20)
+#define CURLY_B_min_known_fail (REGNODE_MAX+21)
+#define CURLY_B_min            (REGNODE_MAX+22)
+#define CURLY_B_min_fail       (REGNODE_MAX+23)
+#define CURLY_B_max            (REGNODE_MAX+24)
+#define CURLY_B_max_fail       (REGNODE_MAX+25)
 
 
 #define REG_NODE_NUM(x) ((x) ? (int)((x)-prog) : -1)
 
-#ifdef DEBUGGING 
-STATIC void 
+#ifdef DEBUGGING
+
+STATIC void
 S_dump_exec_pos(pTHX_ const char *locinput, const regnode *scan, const bool do_utf8)
 {
     const int docolor = *PL_colors[0];
@@ -2662,24 +2647,30 @@ S_dump_exec_pos(pTHX_ const char *locinput, const regnode *scan, const bool do_u
     if (pref0_len > pref_len)
        pref0_len = pref_len;
     {
-      const char * const s0 =
-       do_utf8 && OP(scan) != CANY ?
-       pv_uni_display(PERL_DEBUG_PAD(0), (U8*)(locinput - pref_len),
-                      pref0_len, 60, UNI_DISPLAY_REGEX) :
-       locinput - pref_len;
-      const int len0 = do_utf8 ? (int)strlen(s0) : pref0_len;
-      const char * const s1 = do_utf8 && OP(scan) != CANY ?
-       pv_uni_display(PERL_DEBUG_PAD(1),
-                      (U8*)(locinput - pref_len + pref0_len),
-                      pref_len - pref0_len, 60, UNI_DISPLAY_REGEX) :
-       locinput - pref_len + pref0_len;
-      const int len1 = do_utf8 ? (int)strlen(s1) : pref_len - pref0_len;
-      const char * const s2 = do_utf8 && OP(scan) != CANY ?
-       pv_uni_display(PERL_DEBUG_PAD(2), (U8*)locinput,
-                      PL_regeol - locinput, 60, UNI_DISPLAY_REGEX) :
-       locinput;
-      const int len2 = do_utf8 ? (int)strlen(s2) : l;
-      PerlIO_printf(Perl_debug_log,
+       const int is_uni = (do_utf8 && OP(scan) != CANY) ? 1 : 0;
+       const char * const s0 = is_uni ?
+           pv_uni_display(PERL_DEBUG_PAD(0), (U8*)(locinput - pref_len),
+                   pref0_len, 60, UNI_DISPLAY_REGEX) :
+           pv_escape(PERL_DEBUG_PAD(0), (locinput - pref_len),
+                   pref0_len, 60, 0);
+
+       const int len0 = strlen(s0);
+       const char * const s1 = is_uni ?
+           pv_uni_display(PERL_DEBUG_PAD(1),
+                   (U8*)(locinput - pref_len + pref0_len),
+                   pref_len - pref0_len, 60, UNI_DISPLAY_REGEX) :
+           pv_escape(PERL_DEBUG_PAD(1),
+                   (locinput - pref_len + pref0_len),
+                   pref_len - pref0_len, 60, 0);
+
+       const int len1 = (int)strlen(s1);
+       const char * const s2 = is_uni ?
+           pv_uni_display(PERL_DEBUG_PAD(2), (U8*)locinput,
+                   PL_regeol - locinput, 60, UNI_DISPLAY_REGEX) :
+           pv_escape(PERL_DEBUG_PAD(2), locinput,
+                   PL_regeol - locinput, 60, 0);
+       const int len2 = (int)strlen(s2);
+       PerlIO_printf(Perl_debug_log,
                    "%4"IVdf" <%s%.*s%s%s%.*s%s%s%s%.*s%s>%*s|",
                    (IV)(locinput - PL_bostr),
                    PL_colors[4],
@@ -2696,6 +2687,7 @@ S_dump_exec_pos(pTHX_ const char *locinput, const regnode *scan, const bool do_u
                    "");
     }
 }
+
 #endif
 
 STATIC I32                     /* 0 failure, 1 success */
@@ -2849,6 +2841,10 @@ S_regmatch(pTHX_ const regmatch_info *reginfo, regnode *prog)
            else
                nextchr = UCHARAT(++locinput);
            break;
+
+#undef  ST
+#define ST st->u.trie
+
        case TRIE:
            {
                 /* what type of TRIE am I? (utf8 makes this contextual) */
@@ -2861,6 +2857,23 @@ S_regmatch(pTHX_ const regmatch_info *reginfo, regnode *prog)
                reg_trie_data * const trie
                    = (reg_trie_data*)rex->data->data[ ARG( scan ) ];
                 U32 state = trie->startstate;
+
+               U8 *uc = ( U8* )locinput;
+               U16 charid = 0;
+               U32 base = 0;
+               UV uvc = 0;
+               STRLEN len = 0;
+               STRLEN foldlen = 0;
+               U8 *uscan = (U8*)NULL;
+               STRLEN bufflen=0;
+               SV *sv_accept_buff = NULL;
+               U8 foldbuf[ UTF8_MAXBYTES_CASE + 1 ];
+
+               ST.accepted = 0; /* how many accepting states we have seen */
+               ST.B = next;
+#ifdef DEBUGGING
+               ST.me = scan;
+#endif
                 
                if (trie->bitmap && trie_type != trie_utf8_fold &&
                    !TRIE_BITMAP_TEST(trie,*locinput)
@@ -2881,30 +2894,16 @@ S_regmatch(pTHX_ const regmatch_info *reginfo, regnode *prog)
                        sayNO_SILENT;
                   }
                 }
-           {
+
                /*
                   traverse the TRIE keeping track of all accepting states
                   we transition through until we get to a failing node.
                */
 
-               U8 *uc = ( U8* )locinput;
-               U16 charid = 0;
-               U32 base = 0;
-               UV uvc = 0;
-               STRLEN len = 0;
-               STRLEN foldlen = 0;
-               U8 *uscan = (U8*)NULL;
-               STRLEN bufflen=0;
-               SV *sv_accept_buff = NULL;
-               U8 foldbuf[ UTF8_MAXBYTES_CASE + 1 ];
-
-               st->u.trie.accepted = 0; /* how many accepting states we have seen */
-               result = 0;
-
                while ( state && uc <= (U8*)PL_regeol ) {
 
                    if (trie->states[ state ].wordnum) {
-                       if (!st->u.trie.accepted ) {
+                       if (!ST.accepted ) {
                            ENTER;
                            SAVETMPS;
                            bufflen = TRIE_INITAL_ACCEPT_BUFFLEN;
@@ -2914,22 +2913,23 @@ S_regmatch(pTHX_ const regmatch_info *reginfo, regnode *prog)
                                                sizeof(reg_trie_accepted));
                            SvPOK_on(sv_accept_buff);
                            sv_2mortal(sv_accept_buff);
-                           st->u.trie.accept_buff =
+                           SAVETMPS;
+                           ST.accept_buff =
                                (reg_trie_accepted*)SvPV_nolen(sv_accept_buff );
                        }
                        else {
-                           if (st->u.trie.accepted >= bufflen) {
+                           if (ST.accepted >= bufflen) {
                                bufflen *= 2;
-                               st->u.trie.accept_buff =(reg_trie_accepted*)
+                               ST.accept_buff =(reg_trie_accepted*)
                                    SvGROW(sv_accept_buff,
                                        bufflen * sizeof(reg_trie_accepted));
                            }
                            SvCUR_set(sv_accept_buff,SvCUR(sv_accept_buff)
                                + sizeof(reg_trie_accepted));
                        }
-                       st->u.trie.accept_buff[st->u.trie.accepted].wordnum = trie->states[state].wordnum;
-                       st->u.trie.accept_buff[st->u.trie.accepted].endpos = uc;
-                       ++st->u.trie.accepted;
+                       ST.accept_buff[ST.accepted].wordnum = trie->states[state].wordnum;
+                       ST.accept_buff[ST.accepted].endpos = uc;
+                       ++ST.accepted;
                    }
 
                    base = trie->states[ state ].trans.base;
@@ -2939,7 +2939,7 @@ S_regmatch(pTHX_ const regmatch_info *reginfo, regnode *prog)
                                PerlIO_printf( Perl_debug_log,
                                    "%*s  %sState: %4"UVxf", Base: %4"UVxf", Accepted: %4"UVxf" ",
                                    2+PL_regindent * 2, "", PL_colors[4],
-                                   (UV)state, (UV)base, (UV)st->u.trie.accepted );
+                                   (UV)state, (UV)base, (UV)ST.accepted );
                    });
 
                    if ( base ) {
@@ -3004,105 +3004,108 @@ S_regmatch(pTHX_ const regmatch_info *reginfo, regnode *prog)
                            charid, uvc, (UV)state, PL_colors[5] );
                    );
                }
-               if (!st->u.trie.accepted )
+               if (!ST.accepted )
                   sayNO;
 
+               DEBUG_EXECUTE_r(
+                   PerlIO_printf( Perl_debug_log,
+                       "%*s  %sgot %"IVdf" possible matches%s\n",
+                       REPORT_CODE_OFF + PL_regindent * 2, "",
+                       PL_colors[4], (IV)ST.accepted, PL_colors[5] );
+               );
+           }
+
+           /* FALL THROUGH */
+
+       case TRIE_next_fail: /* we failed - try next alterative */
+
+           if ( ST.accepted == 1 ) {
+               /* only one choice left - just continue */
+               DEBUG_EXECUTE_r({
+                   reg_trie_data * const trie
+                       = (reg_trie_data*)rex->data->data[ ARG(ST.me) ];
+                   SV ** const tmp = RX_DEBUG(reginfo->prog)
+                                   ? av_fetch( trie->words, ST.accept_buff[ 0 ].wordnum-1, 0 )
+                                   : NULL;
+                   PerlIO_printf( Perl_debug_log,
+                       "%*s  %sonly one match left: #%d <%s>%s\n",
+                       REPORT_CODE_OFF+PL_regindent*2, "", PL_colors[4],
+                       ST.accept_buff[ 0 ].wordnum,
+                       tmp ? SvPV_nolen_const( *tmp ) : "not compiled under -Dr",
+                       PL_colors[5] );
+               });
+               PL_reginput = (char *)ST.accept_buff[ 0 ].endpos;
+               /* in this case we free tmps/leave before we call regmatch
+                  as we wont be using accept_buff again. */
+               FREETMPS;
+               LEAVE;
+               locinput = PL_reginput;
+               nextchr = UCHARAT(locinput);
+               scan = ST.B;
+               continue; /* execute rest of RE */
+           }
+
+           if (!ST.accepted-- ) {
+               FREETMPS;
+               LEAVE;
+               sayNO;
+           }
+
            /*
-              There was at least one accepting state that we
-              transitioned through. Presumably the number of accepting
-              states is going to be low, typically one or two. So we
-              simply scan through to find the one with lowest wordnum.
-              Once we find it, we swap the last state into its place
-              and decrement the size. We then try to match the rest of
-              the pattern at the point where the word ends, if we
-              succeed then we end the loop, otherwise the loop
-              eventually terminates once all of the accepting states
-              have been tried.
-           */
+              There are at least two accepting states left.  Presumably
+              the number of accepting states is going to be low,
+              typically two. So we simply scan through to find the one
+              with lowest wordnum.  Once we find it, we swap the last
+              state into its place and decrement the size. We then try to
+              match the rest of the pattern at the point where the word
+              ends. If we succeed, control just continues along the
+              regex; if we fail we return here to try the next accepting
+              state
+            */
 
-               if ( st->u.trie.accepted == 1 ) {
-                   DEBUG_EXECUTE_r({
-                       SV ** const tmp = RX_DEBUG(reginfo->prog)
-                                       ? av_fetch( trie->words, st->u.trie.accept_buff[ 0 ].wordnum-1, 0 )
-                                       : NULL;
+           {
+               U32 best = 0;
+               U32 cur;
+               for( cur = 1 ; cur <= ST.accepted ; cur++ ) {
+                   DEBUG_TRIE_EXECUTE_r(
                        PerlIO_printf( Perl_debug_log,
-                           "%*s  %sonly one match : #%d <%s>%s\n",
-                           REPORT_CODE_OFF+PL_regindent*2, "", PL_colors[4],
-                           st->u.trie.accept_buff[ 0 ].wordnum,
-                           tmp ? SvPV_nolen_const( *tmp ) : "not compiled under -Dr",
-                           PL_colors[5] );
-                   });
-                   PL_reginput = (char *)st->u.trie.accept_buff[ 0 ].endpos;
-                   /* in this case we free tmps/leave before we call regmatch
-                      as we wont be using accept_buff again. */
-                   FREETMPS;
-                   LEAVE;
-                   /* do we need this? why dont we just do a break? */
-                   REGMATCH(scan + NEXT_OFF(scan), TRIE1);
-                   /*** all unsaved local vars undefined at this point */
-               } else {
-                    DEBUG_EXECUTE_r(
-                        PerlIO_printf( Perl_debug_log,"%*s  %sgot %"IVdf" possible matches%s\n",
-                            REPORT_CODE_OFF + PL_regindent * 2, "", PL_colors[4], (IV)st->u.trie.accepted,
-                            PL_colors[5] );
-                    );
-                   while ( !result && st->u.trie.accepted-- ) {
-                       U32 best = 0;
-                       U32 cur;
-                       for( cur = 1 ; cur <= st->u.trie.accepted ; cur++ ) {
-                           DEBUG_TRIE_EXECUTE_r(
-                               PerlIO_printf( Perl_debug_log,
-                                   "%*s  %sgot %"IVdf" (%d) as best, looking at %"IVdf" (%d)%s\n",
-                                   REPORT_CODE_OFF + PL_regindent * 2, "", PL_colors[4],
-                                   (IV)best, st->u.trie.accept_buff[ best ].wordnum, (IV)cur,
-                                   st->u.trie.accept_buff[ cur ].wordnum, PL_colors[5] );
-                           );
-
-                           if (st->u.trie.accept_buff[cur].wordnum <
-                                   st->u.trie.accept_buff[best].wordnum)
-                               best = cur;
-                       }
-                       DEBUG_EXECUTE_r({
-                           reg_trie_data * const trie = (reg_trie_data*)
-                                           rex->data->data[ARG(scan)];
-                           SV ** const tmp = RX_DEBUG(reginfo->prog)
-                                       ? av_fetch( trie->words, st->u.trie.accept_buff[ best ].wordnum - 1, 0 )
-                                       : NULL;
-                           PerlIO_printf( Perl_debug_log, "%*s  %strying alternation #%d <%s> at node #%d %s\n",
-                               REPORT_CODE_OFF+PL_regindent*2, "", PL_colors[4],
-                               st->u.trie.accept_buff[best].wordnum,
-                               tmp ? SvPV_nolen_const( *tmp ) : "not compiled under -Dr", REG_NODE_NUM(scan),
-                               PL_colors[5] );
-                       });
-                       if ( best<st->u.trie.accepted ) {
-                           reg_trie_accepted tmp = st->u.trie.accept_buff[ best ];
-                           st->u.trie.accept_buff[ best ] = st->u.trie.accept_buff[ st->u.trie.accepted ];
-                           st->u.trie.accept_buff[ st->u.trie.accepted ] = tmp;
-                           best = st->u.trie.accepted;
-                       }
-                       PL_reginput = (char *)st->u.trie.accept_buff[ best ].endpos;
-
-                        /* 
-                           as far as I can tell we only need the SAVETMPS/FREETMPS 
-                           for re's with EVAL in them but I'm leaving them in for 
-                           all until I can be sure.
-                         */
-                       SAVETMPS;
-                       REGMATCH(scan + NEXT_OFF(scan), TRIE2);
-                       /*** all unsaved local vars undefined at this point */
-                       FREETMPS;
-                   }
-                   FREETMPS;
-                   LEAVE;
+                           "%*s  %sgot %"IVdf" (%d) as best, looking at %"IVdf" (%d)%s\n",
+                           REPORT_CODE_OFF + PL_regindent * 2, "", PL_colors[4],
+                           (IV)best, ST.accept_buff[ best ].wordnum, (IV)cur,
+                           ST.accept_buff[ cur ].wordnum, PL_colors[5] );
+                   );
+
+                   if (ST.accept_buff[cur].wordnum <
+                           ST.accept_buff[best].wordnum)
+                       best = cur;
                }
-               
-               if (result) {
-                   sayYES;
-               } else {
-                   sayNO;
+
+               DEBUG_EXECUTE_r({
+                   reg_trie_data * const trie
+                       = (reg_trie_data*)rex->data->data[ ARG(ST.me) ];
+                   SV ** const tmp = RX_DEBUG(reginfo->prog)
+                               ? av_fetch( trie->words, ST.accept_buff[ best ].wordnum - 1, 0 )
+                               : NULL;
+                   PerlIO_printf( Perl_debug_log, "%*s  %strying alternation #%d <%s> at node #%d %s\n",
+                       REPORT_CODE_OFF+PL_regindent*2, "", PL_colors[4],
+                       ST.accept_buff[best].wordnum,
+                       tmp ? SvPV_nolen_const( *tmp ) : "not compiled under -Dr", REG_NODE_NUM(scan),
+                       PL_colors[5] );
+               });
+
+               if ( best<ST.accepted ) {
+                   reg_trie_accepted tmp = ST.accept_buff[ best ];
+                   ST.accept_buff[ best ] = ST.accept_buff[ ST.accepted ];
+                   ST.accept_buff[ ST.accepted ] = tmp;
+                   best = ST.accepted;
                }
-           }}
-           /* unreached codepoint */
+               PL_reginput = (char *)ST.accept_buff[ best ].endpos;
+           }
+           PUSH_STATE_GOTO(TRIE_next, ST.B);
+           /* NOTREACHED */
+
+#undef  ST
+
        case EXACT: {
            char *s = STRING(scan);
            st->ln = STR_LEN(scan);
@@ -4229,46 +4232,58 @@ S_regmatch(pTHX_ const regmatch_info *reginfo, regnode *prog)
            locinput = HOPc(locinput, -ST.alen);
            goto curlym_do_B; /* try to match B */
 
+#undef ST
+#define ST st->u.curly
+
+#define CURLY_SETPAREN(paren, success) \
+    if (paren) { \
+       if (success) { \
+           PL_regstartp[paren] = HOPc(locinput, -1) - PL_bostr; \
+           PL_regendp[paren] = locinput - PL_bostr; \
+       } \
+       else \
+           PL_regendp[paren] = -1; \
+    }
 
-       case CURLYN:
-           st->u.plus.paren = scan->flags;     /* Which paren to set */
-           if (st->u.plus.paren > PL_regsize)
-               PL_regsize = st->u.plus.paren;
-           if (st->u.plus.paren > (I32)*PL_reglastparen)
-               *PL_reglastparen = st->u.plus.paren;
-           st->ln = ARG1(scan);  /* min to match */
-           n  = ARG2(scan);  /* max to match */
-            scan = regnext(NEXTOPER(scan) + NODE_STEP_REGNODE);
-           goto repeat;
-       case CURLY:
-           st->u.plus.paren = 0;
-           st->ln = ARG1(scan);  /* min to match */
-           n  = ARG2(scan);  /* max to match */
-           scan = NEXTOPER(scan) + NODE_STEP_REGNODE;
-           goto repeat;
-       case STAR:
-           st->ln = 0;
-           n = REG_INFTY;
+       case STAR:              /*  /A*B/ where A is width 1 */
+           ST.paren = 0;
+           ST.min = 0;
+           ST.max = REG_INFTY;
            scan = NEXTOPER(scan);
-           st->u.plus.paren = 0;
            goto repeat;
-       case PLUS:
-           st->ln = 1;
-           n = REG_INFTY;
+       case PLUS:              /*  /A+B/ where A is width 1 */
+           ST.paren = 0;
+           ST.min = 1;
+           ST.max = REG_INFTY;
            scan = NEXTOPER(scan);
-           st->u.plus.paren = 0;
+           goto repeat;
+       case CURLYN:            /*  /(A){m,n}B/ where A is width 1 */
+           ST.paren = scan->flags;     /* Which paren to set */
+           if (ST.paren > PL_regsize)
+               PL_regsize = ST.paren;
+           if (ST.paren > (I32)*PL_reglastparen)
+               *PL_reglastparen = ST.paren;
+           ST.min = ARG1(scan);  /* min to match */
+           ST.max = ARG2(scan);  /* max to match */
+            scan = regnext(NEXTOPER(scan) + NODE_STEP_REGNODE);
+           goto repeat;
+       case CURLY:             /*  /A{m,n}B/ where A is width 1 */
+           ST.paren = 0;
+           ST.min = ARG1(scan);  /* min to match */
+           ST.max = ARG2(scan);  /* max to match */
+           scan = NEXTOPER(scan) + NODE_STEP_REGNODE;
          repeat:
            /*
            * Lookahead to avoid useless match attempts
            * when we know what character comes next.
-           */
-
-           /*
+           *
            * Used to only do .*x and .*?x, but now it allows
            * for )'s, ('s and (?{ ... })'s to be in the way
            * of the quantifier and the EXACT-like node.  -- japhy
            */
 
+           if (ST.min > ST.max) /* XXX make this a compile-time check? */
+               sayNO;
            if (HAS_TEXT(next) || JUMPABLE(next)) {
                U8 *s;
                regnode *text_node = next;
@@ -4277,21 +4292,21 @@ S_regmatch(pTHX_ const regmatch_info *reginfo, regnode *prog)
                    FIND_NEXT_IMPT(text_node);
 
                if (! HAS_TEXT(text_node))
-                   st->u.plus.c1 = st->u.plus.c2 = CHRTEST_VOID;
+                   ST.c1 = ST.c2 = CHRTEST_VOID;
                else {
                    if (PL_regkind[OP(text_node)] == REF) {
-                       st->u.plus.c1 = st->u.plus.c2 = CHRTEST_VOID;
+                       ST.c1 = ST.c2 = CHRTEST_VOID;
                        goto assume_ok_easy;
                    }
                    else
                        s = (U8*)STRING(text_node);
 
                    if (!UTF) {
-                       st->u.plus.c2 = st->u.plus.c1 = *s;
+                       ST.c2 = ST.c1 = *s;
                        if (OP(text_node) == EXACTF || OP(text_node) == REFF)
-                           st->u.plus.c2 = PL_fold[st->u.plus.c1];
+                           ST.c2 = PL_fold[ST.c1];
                        else if (OP(text_node) == EXACTFL || OP(text_node) == REFFL)
-                           st->u.plus.c2 = PL_fold_locale[st->u.plus.c1];
+                           ST.c2 = PL_fold_locale[ST.c1];
                    }
                    else { /* UTF */
                        if (OP(text_node) == EXACTF || OP(text_node) == REFF) {
@@ -4302,186 +4317,210 @@ S_regmatch(pTHX_ const regmatch_info *reginfo, regnode *prog)
                             to_utf8_lower((U8*)s, tmpbuf1, &ulen1);
                             to_utf8_upper((U8*)s, tmpbuf2, &ulen2);
 
-                            st->u.plus.c1 = utf8n_to_uvuni(tmpbuf1, UTF8_MAXBYTES, 0,
+                            ST.c1 = utf8n_to_uvuni(tmpbuf1, UTF8_MAXBYTES, 0,
                                                 uniflags);
-                            st->u.plus.c2 = utf8n_to_uvuni(tmpbuf2, UTF8_MAXBYTES, 0,
+                            ST.c2 = utf8n_to_uvuni(tmpbuf2, UTF8_MAXBYTES, 0,
                                                 uniflags);
                        }
                        else {
-                           st->u.plus.c2 = st->u.plus.c1 = utf8n_to_uvchr(s, UTF8_MAXBYTES, 0,
+                           ST.c2 = ST.c1 = utf8n_to_uvchr(s, UTF8_MAXBYTES, 0,
                                                     uniflags);
                        }
                    }
                }
            }
            else
-               st->u.plus.c1 = st->u.plus.c2 = CHRTEST_VOID;
+               ST.c1 = ST.c2 = CHRTEST_VOID;
        assume_ok_easy:
+
+           ST.A = scan;
+           ST.B = next;
            PL_reginput = locinput;
            if (st->minmod) {
                st->minmod = 0;
-               if (st->ln && regrepeat(rex, scan, st->ln) < st->ln)
+               if (ST.min && regrepeat(rex, ST.A, ST.min) < ST.min)
                    sayNO;
+               ST.count = ST.min;
                locinput = PL_reginput;
-               REGCP_SET(st->u.plus.lastcp);
-               if (st->u.plus.c1 != CHRTEST_VOID) {
-                   st->u.plus.old = locinput;
-                   st->u.plus.count = 0;
-
-                   if  (n == REG_INFTY) {
-                       st->u.plus.e = PL_regeol - 1;
-                       if (do_utf8)
-                           while (UTF8_IS_CONTINUATION(*(U8*)st->u.plus.e))
-                               st->u.plus.e--;
-                   }
-                   else if (do_utf8) {
-                       int m = n - st->ln;
-                       for (st->u.plus.e = locinput;
-                            m >0 && st->u.plus.e + UTF8SKIP(st->u.plus.e) <= PL_regeol; m--)
-                           st->u.plus.e += UTF8SKIP(st->u.plus.e);
+               REGCP_SET(ST.cp);
+               if (ST.c1 == CHRTEST_VOID)
+                   goto curly_try_B_min;
+
+               ST.oldloc = locinput;
+
+               /* set ST.maxpos to the furthest point along the
+                * string that could possibly match */
+               if  (ST.max == REG_INFTY) {
+                   ST.maxpos = PL_regeol - 1;
+                   if (do_utf8)
+                       while (UTF8_IS_CONTINUATION(*(U8*)ST.maxpos))
+                           ST.maxpos--;
+               }
+               else if (do_utf8) {
+                   int m = ST.max - ST.min;
+                   for (ST.maxpos = locinput;
+                        m >0 && ST.maxpos + UTF8SKIP(ST.maxpos) <= PL_regeol; m--)
+                       ST.maxpos += UTF8SKIP(ST.maxpos);
+               }
+               else {
+                   ST.maxpos = locinput + ST.max - ST.min;
+                   if (ST.maxpos >= PL_regeol)
+                       ST.maxpos = PL_regeol - 1;
+               }
+               goto curly_try_B_min_known;
+
+           }
+           else {
+               ST.count = regrepeat(rex, ST.A, ST.max);
+               locinput = PL_reginput;
+               if (ST.count < ST.min)
+                   sayNO;
+               if ((ST.count > ST.min)
+                   && (PL_regkind[OP(ST.B)] == EOL) && (OP(ST.B) != MEOL))
+               {
+                   /* A{m,n} must come at the end of the string, there's
+                    * no point in backing off ... */
+                   ST.min = ST.count;
+                   /* ...except that $ and \Z can match before *and* after
+                      newline at the end.  Consider "\n\n" =~ /\n+\Z\n/.
+                      We may back off by one in this case. */
+                   if (UCHARAT(PL_reginput - 1) == '\n' && OP(ST.B) != EOS)
+                       ST.min--;
+               }
+               REGCP_SET(ST.cp);
+               goto curly_try_B_max;
+           }
+           /* NOTREACHED */
+
+
+       case CURLY_B_min_known_fail:
+           /* failed to find B in a non-greedy match where c1,c2 valid */
+           if (ST.paren && ST.count)
+               PL_regendp[ST.paren] = -1;
+
+           PL_reginput = locinput;     /* Could be reset... */
+           REGCP_UNWIND(ST.cp);
+           /* Couldn't or didn't -- move forward. */
+           ST.oldloc = locinput;
+           if (do_utf8)
+               locinput += UTF8SKIP(locinput);
+           else
+               locinput++;
+           ST.count++;
+         curly_try_B_min_known:
+            /* find the next place where 'B' could work, then call B */
+           {
+               int n;
+               if (do_utf8) {
+                   n = (ST.oldloc == locinput) ? 0 : 1;
+                   if (ST.c1 == ST.c2) {
+                       STRLEN len;
+                       /* set n to utf8_distance(oldloc, locinput) */
+                       while (locinput <= ST.maxpos &&
+                              utf8n_to_uvchr((U8*)locinput,
+                                             UTF8_MAXBYTES, &len,
+                                             uniflags) != (UV)ST.c1) {
+                           locinput += len;
+                           n++;
+                       }
                    }
                    else {
-                       st->u.plus.e = locinput + n - st->ln;
-                       if (st->u.plus.e >= PL_regeol)
-                           st->u.plus.e = PL_regeol - 1;
-                   }
-                   while (1) {
-                       /* Find place 'next' could work */
-                       if (!do_utf8) {
-                           if (st->u.plus.c1 == st->u.plus.c2) {
-                               while (locinput <= st->u.plus.e &&
-                                      UCHARAT(locinput) != st->u.plus.c1)
-                                   locinput++;
-                           } else {
-                               while (locinput <= st->u.plus.e
-                                      && UCHARAT(locinput) != st->u.plus.c1
-                                      && UCHARAT(locinput) != st->u.plus.c2)
-                                   locinput++;
-                           }
-                           st->u.plus.count = locinput - st->u.plus.old;
-                       }
-                       else {
-                           if (st->u.plus.c1 == st->u.plus.c2) {
-                               STRLEN len;
-                               /* count initialised to
-                                * utf8_distance(old, locinput) */
-                               while (locinput <= st->u.plus.e &&
-                                      utf8n_to_uvchr((U8*)locinput,
-                                                     UTF8_MAXBYTES, &len,
-                                                     uniflags) != (UV)st->u.plus.c1) {
-                                   locinput += len;
-                                   st->u.plus.count++;
-                               }
-                           } else {
-                               /* count initialised to
-                                * utf8_distance(old, locinput) */
-                               while (locinput <= st->u.plus.e) {
-                                   STRLEN len;
-                                   const UV c = utf8n_to_uvchr((U8*)locinput,
-                                                         UTF8_MAXBYTES, &len,
-                                                         uniflags);
-                                   if (c == (UV)st->u.plus.c1 || c == (UV)st->u.plus.c2)
-                                       break;
-                                   locinput += len;
-                                   st->u.plus.count++;
-                               }
-                           }
-                       }
-                       if (locinput > st->u.plus.e)
-                           sayNO;
-                       /* PL_reginput == old now */
-                       if (locinput != st->u.plus.old) {
-                           st->ln = 1; /* Did some */
-                           if (regrepeat(rex, scan, st->u.plus.count) < st->u.plus.count)
-                               sayNO;
+                       /* set n to utf8_distance(oldloc, locinput) */
+                       while (locinput <= ST.maxpos) {
+                           STRLEN len;
+                           const UV c = utf8n_to_uvchr((U8*)locinput,
+                                                 UTF8_MAXBYTES, &len,
+                                                 uniflags);
+                           if (c == (UV)ST.c1 || c == (UV)ST.c2)
+                               break;
+                           locinput += len;
+                           n++;
                        }
-                       /* PL_reginput == locinput now */
-                       PL_reginput = locinput; /* Could be reset... */
-                       TRYPAREN(st->u.plus.paren, st->ln, locinput, PLUS1);
-                       /*** all unsaved local vars undefined at this point */
-
-                       REGCP_UNWIND(st->u.plus.lastcp);
-                       /* Couldn't or didn't -- move forward. */
-                       st->u.plus.old = locinput;
-                       if (do_utf8)
-                           locinput += UTF8SKIP(locinput);
-                       else
-                           locinput++;
-                       st->u.plus.count = 1;
                    }
                }
-               else
-               while (n >= st->ln || (n == REG_INFTY && st->ln > 0)) { /* ln overflow ? */
-                   UV c;
-                   if (st->u.plus.c1 != CHRTEST_VOID) {
-                       if (do_utf8)
-                           c = utf8n_to_uvchr((U8*)PL_reginput,
-                                              UTF8_MAXBYTES, 0,
-                                              uniflags);
-                       else
-                           c = UCHARAT(PL_reginput);
-                       /* If it could work, try it. */
-                       if (c == (UV)st->u.plus.c1 || c == (UV)st->u.plus.c2) {
-                           TRYPAREN(st->u.plus.paren, st->ln, PL_reginput, PLUS2);
-                           /*** all unsaved local vars undefined at this point */
-                           REGCP_UNWIND(st->u.plus.lastcp);
-                       }
-                   }
-                   /* If it could work, try it. */
-                   else if (st->u.plus.c1 == CHRTEST_VOID) {
-                       TRYPAREN(st->u.plus.paren, st->ln, PL_reginput, PLUS3);
-                       /*** all unsaved local vars undefined at this point */
-                       REGCP_UNWIND(st->u.plus.lastcp);
+               else {
+                   if (ST.c1 == ST.c2) {
+                       while (locinput <= ST.maxpos &&
+                              UCHARAT(locinput) != ST.c1)
+                           locinput++;
                    }
-                   /* Couldn't or didn't -- move forward. */
-                   PL_reginput = locinput;
-                   if (regrepeat(rex, scan, 1)) {
-                       st->ln++;
-                       locinput = PL_reginput;
+                   else {
+                       while (locinput <= ST.maxpos
+                              && UCHARAT(locinput) != ST.c1
+                              && UCHARAT(locinput) != ST.c2)
+                           locinput++;
                    }
-                   else
+                   n = locinput - ST.oldloc;
+               }
+               if (locinput > ST.maxpos)
+                   sayNO;
+               /* PL_reginput == oldloc now */
+               if (n) {
+                   ST.count += n;
+                   if (regrepeat(rex, ST.A, n) < n)
                        sayNO;
                }
+               PL_reginput = locinput;
+               CURLY_SETPAREN(ST.paren, ST.count);
+               PUSH_STATE_GOTO(CURLY_B_min_known, ST.B);
            }
-           else {
-               n = regrepeat(rex, scan, n);
+           /* NOTREACHED */
+
+
+       case CURLY_B_min_fail:
+           /* failed to find B in a non-greedy match where c1,c2 invalid */
+           if (ST.paren && ST.count)
+               PL_regendp[ST.paren] = -1;
+
+           REGCP_UNWIND(ST.cp);
+           /* failed -- move forward one */
+           PL_reginput = locinput;
+           if (regrepeat(rex, ST.A, 1)) {
+               ST.count++;
                locinput = PL_reginput;
-               if ((st->ln < n) && (PL_regkind[OP(next)] == EOL) &&
-                   (OP(next) != MEOL || OP(next) == SEOL || OP(next) == EOS))
-               {
-                   st->ln = n;                 /* why back off? */
-                   /* ...because $ and \Z can match before *and* after
-                      newline at the end.  Consider "\n\n" =~ /\n+\Z\n/.
-                      We should back off by one in this case. */
-                   if (UCHARAT(PL_reginput - 1) == '\n' && OP(next) != EOS)
-                       st->ln--;
-               }
-               REGCP_SET(st->u.plus.lastcp);
+               if (ST.count <= ST.max || (ST.max == REG_INFTY &&
+                       ST.count > 0)) /* count overflow ? */
                {
-                   while (n >= st->ln) {
-                       UV c = 0;
-                       if (st->u.plus.c1 != CHRTEST_VOID) {
-                           if (do_utf8)
-                               c = utf8n_to_uvchr((U8*)PL_reginput,
-                                                  UTF8_MAXBYTES, 0,
-                                                  uniflags);
-                           else
-                               c = UCHARAT(PL_reginput);
-                       }
-                       /* If it could work, try it. */
-                       if (st->u.plus.c1 == CHRTEST_VOID || c == (UV)st->u.plus.c1 || c == (UV)st->u.plus.c2) {
-                           TRYPAREN(st->u.plus.paren, n, PL_reginput, PLUS4);
-                           /*** all unsaved local vars undefined at this point */
-                           REGCP_UNWIND(st->u.plus.lastcp);
-                       }
-                       /* Couldn't or didn't -- back up. */
-                       n--;
-                       PL_reginput = locinput = HOPc(locinput, -1);
-                   }
+                 curly_try_B_min:
+                   CURLY_SETPAREN(ST.paren, ST.count);
+                   PUSH_STATE_GOTO(CURLY_B_min, ST.B);
                }
            }
            sayNO;
-           break;
+           /* NOTREACHED */
+
+
+       curly_try_B_max:
+           /* a successful greedy match: now try to match B */
+           {
+               UV c = 0;
+               if (ST.c1 != CHRTEST_VOID)
+                   c = do_utf8 ? utf8n_to_uvchr((U8*)PL_reginput,
+                                          UTF8_MAXBYTES, 0, uniflags)
+                               : (UV) UCHARAT(PL_reginput);
+               /* If it could work, try it. */
+               if (ST.c1 == CHRTEST_VOID || c == (UV)ST.c1 || c == (UV)ST.c2) {
+                   CURLY_SETPAREN(ST.paren, ST.count);
+                   PUSH_STATE_GOTO(CURLY_B_max, ST.B);
+                   /* NOTREACHED */
+               }
+           }
+           /* FALL THROUGH */
+       case CURLY_B_max_fail:
+           /* failed to find B in a greedy match */
+           if (ST.paren && ST.count)
+               PL_regendp[ST.paren] = -1;
+
+           REGCP_UNWIND(ST.cp);
+           /*  back up. */
+           if (--ST.count < ST.min)
+               sayNO;
+           PL_reginput = locinput = HOPc(locinput, -1);
+           goto curly_try_B_max;
+
+#undef ST
+
+
        case END:
            if (locinput < reginfo->till) {
                DEBUG_EXECUTE_r(PerlIO_printf(Perl_debug_log,
@@ -4685,12 +4724,14 @@ yes_final:
        switch (st->resume_state) {
        case IFMATCH_A:
        case CURLYM_A:
-       case BRANCH_next:
        case EVAL_A:
            state_num = st->resume_state;
            goto reenter_switch;
 
        case CURLYM_B:
+       case BRANCH_next:
+       case TRIE_next:
+       case CURLY_B_max:
        default:
            Perl_croak(aTHX_ "unexpected yes resume state");
        }
@@ -4723,10 +4764,6 @@ yes:
        nextchr = UCHARAT(locinput);
 
        switch (st->resume_state) {
-       case resume_TRIE1:
-           goto resume_point_TRIE1;
-       case resume_TRIE2:
-           goto resume_point_TRIE2;
        case resume_CURLYX:
            goto resume_point_CURLYX;
        case resume_WHILEM1:
@@ -4741,20 +4778,16 @@ yes:
            goto resume_point_WHILEM5;
        case resume_WHILEM6:
            goto resume_point_WHILEM6;
-       case resume_PLUS1:
-           goto resume_point_PLUS1;
-       case resume_PLUS2:
-           goto resume_point_PLUS2;
-       case resume_PLUS3:
-           goto resume_point_PLUS3;
-       case resume_PLUS4:
-           goto resume_point_PLUS4;
 
+       case TRIE_next:
        case CURLYM_A:
        case CURLYM_B:
        case EVAL_A:
        case IFMATCH_A:
        case BRANCH_next:
+       case CURLY_B_max:
+       case CURLY_B_min:
+       case CURLY_B_min_known:
            break;
 
        default:
@@ -4794,11 +4827,6 @@ do_no:
        nextchr = UCHARAT(locinput);
 
        switch (st->resume_state) {
-       case resume_TRIE1:
-           goto resume_point_TRIE1;
-       case resume_TRIE2:
-           goto resume_point_TRIE2;
-
        case resume_CURLYX:
            goto resume_point_CURLYX;
        case resume_WHILEM1:
@@ -4814,24 +4842,20 @@ do_no:
        case resume_WHILEM6:
            goto resume_point_WHILEM6;
 
+       case TRIE_next:
        case EVAL_A:
        case BRANCH_next:
        case CURLYM_A:
        case CURLYM_B:
        case IFMATCH_A:
+       case CURLY_B_max:
+       case CURLY_B_min:
+       case CURLY_B_min_known:
            if (yes_state == st)
                yes_state = st->u.yes.prev_yes_state;
            state_num = st->resume_state + 1; /* failure = success + 1 */
            goto reenter_switch;
 
-       case resume_PLUS1:
-           goto resume_point_PLUS1;
-       case resume_PLUS2:
-           goto resume_point_PLUS2;
-       case resume_PLUS3:
-           goto resume_point_PLUS3;
-       case resume_PLUS4:
-           goto resume_point_PLUS4;
        default:
            Perl_croak(aTHX_ "regexp resume memory corruption");
        }