summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorph10 <ph10@2f5784b3-3f2a-0410-8824-cb99058d5e15>2009-10-18 10:02:46 +0000
committerph10 <ph10@2f5784b3-3f2a-0410-8824-cb99058d5e15>2009-10-18 10:02:46 +0000
commitbf3e4896d728bafe2515dd61bf484e0b84e84c89 (patch)
tree0325a6585cbb27bb35e9f1ebc8feb43262fb09f4
parent1da028459167ef408e659b9fe91eb70f3b79e395 (diff)
downloadpcre-bf3e4896d728bafe2515dd61bf484e0b84e84c89.tar.gz
Further tidies to partial matching.
git-svn-id: svn://vcs.exim.org/pcre/code/trunk@463 2f5784b3-3f2a-0410-8824-cb99058d5e15
-rw-r--r--doc/pcrepartial.37
-rw-r--r--pcre_dfa_exec.c45
-rw-r--r--pcre_exec.c244
-rw-r--r--testdata/testinput75
-rw-r--r--testdata/testoutput714
5 files changed, 161 insertions, 154 deletions
diff --git a/doc/pcrepartial.3 b/doc/pcrepartial.3
index f0a9b3a..4f66758 100644
--- a/doc/pcrepartial.3
+++ b/doc/pcrepartial.3
@@ -320,7 +320,8 @@ matching multi-segment data. The example above then behaves differently:
.P
4. Patterns that contain alternatives at the top level which do not all
start with the same pattern item may not work as expected when
-\fBpcre_dfa_exec()\fP is used. For example, consider this pattern:
+PCRE_DFA_RESTART is used with \fBpcre_dfa_exec()\fP. For example, consider this
+pattern:
.sp
1234|3789
.sp
@@ -345,6 +346,8 @@ each time:
data> 1237890
0: 3789
.sp
+Of course, instead of using PCRE_DFA_PARTIAL, the same technique of re-running
+the entire match can also be used with \fBpcre_dfa_exec()\fP.
.
.
.SH AUTHOR
@@ -361,6 +364,6 @@ Cambridge CB2 3QH, England.
.rs
.sp
.nf
-Last updated: 29 September 2009
+Last updated: 18 October 2009
Copyright (c) 1997-2009 University of Cambridge.
.fi
diff --git a/pcre_dfa_exec.c b/pcre_dfa_exec.c
index 458bb4c..a4a0ae2 100644
--- a/pcre_dfa_exec.c
+++ b/pcre_dfa_exec.c
@@ -109,7 +109,7 @@ never stored, so we push them well clear of the normal opcodes. */
character that is to be tested in some way. This makes is possible to
centralize the loading of these characters. In the case of Type * etc, the
"character" is the opcode for \D, \d, \S, \s, \W, or \w, which will always be a
-small value. Non-zero values in the table are the offsets from the opcode where
+small value. Non-zero values in the table are the offsets from the opcode where
the character is to be found. ***NOTE*** If the start of this table is
modified, the three tables that follow must also be modified. */
@@ -164,15 +164,14 @@ static const uschar coptable[] = {
0, 0, 0, 0 /* FAIL, ACCEPT, CLOSE, SKIPZERO */
};
-/* This table identifies those opcodes that inspect a character. It is used to
+/* This table identifies those opcodes that inspect a character. It is used to
remember the fact that a character could have been inspected when the end of
-the subject is reached, in order to support PCRE_PARTIAL_HARD behaviour.
-***NOTE*** If the start of this table is modified, the two tables that follow
-must also be modified. */
+the subject is reached. ***NOTE*** If the start of this table is modified, the
+two tables that follow must also be modified. */
static const uschar poptable[] = {
0, /* End */
- 0, 0, 0, 0, 0, /* \A, \G, \K, \B, \b */
+ 0, 0, 0, 1, 1, /* \A, \G, \K, \B, \b */
1, 1, 1, 1, 1, 1, /* \D, \d, \S, \s, \W, \w */
1, 1, 1, /* Any, AllAny, Anybyte */
1, 1, 1, /* NOTPROP, PROP, EXTUNI */
@@ -546,7 +545,6 @@ for (;;)
int clen, dlen;
unsigned int c, d;
int forced_fail = 0;
- int reached_end = 0;
BOOL could_continue = FALSE;
/* Make the new state list into the active state list and empty the
@@ -655,12 +653,12 @@ for (;;)
code = start_code + state_offset;
codevalue = *code;
-
- /* If this opcode inspects a character, but we are at the end of the
- subject, remember the fact so that we can support PCRE_PARTIAL_HARD. */
+
+ /* If this opcode inspects a character, but we are at the end of the
+ subject, remember the fact for use when testing for a partial match. */
if (clen == 0 && poptable[codevalue] != 0)
- could_continue = TRUE;
+ could_continue = TRUE;
/* If this opcode is followed by an inline character, load it. It is
tempting to test for the presence of a subject character here, but that
@@ -729,7 +727,6 @@ for (;;)
}
else
{
- reached_end++; /* Count branches that reach the end */
if (ptr > current_subject ||
((md->moptions & PCRE_NOTEMPTY) == 0 &&
((md->moptions & PCRE_NOTEMPTY_ATSTART) == 0 ||
@@ -915,11 +912,7 @@ for (;;)
if (clen > 0)
right_word = c < 256 && (ctypes[c] & ctype_word) != 0;
- else /* This is a fudge to ensure that if this is the */
- { /* last item in the pattern, we don't count it as */
- reached_end--; /* reached, thus disabling a partial match. */
- right_word = 0;
- }
+ else right_word = 0;
if ((left_word == right_word) == (codevalue == OP_NOT_WORD_BOUNDARY))
{ ADD_ACTIVE(state_offset + 1, 0); }
@@ -2587,24 +2580,20 @@ for (;;)
/* We have finished the processing at the current subject character. If no
new states have been set for the next character, we have found all the
matches that we are going to find. If we are at the top level and partial
- matching has been requested, check for appropriate conditions.
-
+ matching has been requested, check for appropriate conditions.
+
The "forced_ fail" variable counts the number of (*F) encountered for the
character. If it is equal to the original active_count (saved in
workspace[1]) it means that (*F) was found on every active state. In this
- case we don't want to give a partial match.
-
- The "reached_end" variable counts the number of threads that have reached the
- end of the pattern. The "could_continue" variable is true if a thread could
- have continued but for the fact that the end of the subject was reached. */
+ case we don't want to give a partial match.
+
+ The "could_continue" variable is true if a state could have continued but
+ for the fact that the end of the subject was reached. */
if (new_count <= 0)
{
if (rlevel == 1 && /* Top level, and */
- ( /* either... */
- reached_end != workspace[1] || /* Not all reached end */
- could_continue /* or some could go on */
- ) && /* and... */
+ could_continue && /* Some could go on */
forced_fail != workspace[1] && /* Not all forced fail & */
( /* either... */
(md->moptions & PCRE_PARTIAL_HARD) != 0 /* Hard partial */
diff --git a/pcre_exec.c b/pcre_exec.c
index f930095..fb27e17 100644
--- a/pcre_exec.c
+++ b/pcre_exec.c
@@ -2146,11 +2146,11 @@ for (;;)
pp = eptr;
for (i = min; i < max; i++)
{
- if (!match_ref(offset, eptr, length, md, ims))
+ if (!match_ref(offset, eptr, length, md, ims))
{
- CHECK_PARTIAL();
+ CHECK_PARTIAL();
break;
- }
+ }
eptr += length;
}
while (eptr >= pp)
@@ -2319,11 +2319,11 @@ for (;;)
for (i = min; i < max; i++)
{
int len = 1;
- if (eptr >= md->end_subject)
+ if (eptr >= md->end_subject)
{
- SCHECK_PARTIAL();
+ SCHECK_PARTIAL();
break;
- }
+ }
GETCHARLEN(c, eptr, len);
if (c > 255)
{
@@ -2349,11 +2349,11 @@ for (;;)
{
for (i = min; i < max; i++)
{
- if (eptr >= md->end_subject)
+ if (eptr >= md->end_subject)
{
- SCHECK_PARTIAL();
+ SCHECK_PARTIAL();
break;
- }
+ }
c = *eptr;
if ((data[c/8] & (1 << (c&7))) == 0) break;
eptr++;
@@ -2458,11 +2458,11 @@ for (;;)
for (i = min; i < max; i++)
{
int len = 1;
- if (eptr >= md->end_subject)
+ if (eptr >= md->end_subject)
{
- SCHECK_PARTIAL();
+ SCHECK_PARTIAL();
break;
- }
+ }
GETCHARLENTEST(c, eptr, len);
if (!_pcre_xclass(c, data)) break;
eptr += len;
@@ -2701,11 +2701,11 @@ for (;;)
eptr <= md->end_subject - oclength &&
memcmp(eptr, occhars, oclength) == 0) eptr += oclength;
#endif /* SUPPORT_UCP */
- else
+ else
{
- CHECK_PARTIAL();
+ CHECK_PARTIAL();
break;
- }
+ }
}
if (possessive) continue;
@@ -2783,11 +2783,11 @@ for (;;)
pp = eptr;
for (i = min; i < max; i++)
{
- if (eptr >= md->end_subject)
+ if (eptr >= md->end_subject)
{
SCHECK_PARTIAL();
break;
- }
+ }
if (fc != md->lcc[*eptr]) break;
eptr++;
}
@@ -2842,11 +2842,11 @@ for (;;)
pp = eptr;
for (i = min; i < max; i++)
{
- if (eptr >= md->end_subject)
+ if (eptr >= md->end_subject)
{
- SCHECK_PARTIAL();
+ SCHECK_PARTIAL();
break;
- }
+ }
if (fc != *eptr) break;
eptr++;
}
@@ -3059,11 +3059,11 @@ for (;;)
for (i = min; i < max; i++)
{
int len = 1;
- if (eptr >= md->end_subject)
+ if (eptr >= md->end_subject)
{
- SCHECK_PARTIAL();
+ SCHECK_PARTIAL();
break;
- }
+ }
GETCHARLEN(d, eptr, len);
if (d < 256) d = md->lcc[d];
if (fc == d) break;
@@ -3084,11 +3084,11 @@ for (;;)
{
for (i = min; i < max; i++)
{
- if (eptr >= md->end_subject)
+ if (eptr >= md->end_subject)
{
SCHECK_PARTIAL();
break;
- }
+ }
if (fc == md->lcc[*eptr]) break;
eptr++;
}
@@ -3198,11 +3198,11 @@ for (;;)
for (i = min; i < max; i++)
{
int len = 1;
- if (eptr >= md->end_subject)
+ if (eptr >= md->end_subject)
{
- SCHECK_PARTIAL();
+ SCHECK_PARTIAL();
break;
- }
+ }
GETCHARLEN(d, eptr, len);
if (fc == d) break;
eptr += len;
@@ -3222,11 +3222,11 @@ for (;;)
{
for (i = min; i < max; i++)
{
- if (eptr >= md->end_subject)
+ if (eptr >= md->end_subject)
{
- SCHECK_PARTIAL();
+ SCHECK_PARTIAL();
break;
- }
+ }
if (fc == *eptr) break;
eptr++;
}
@@ -4383,11 +4383,11 @@ for (;;)
for (i = min; i < max; i++)
{
int len = 1;
- if (eptr >= md->end_subject)
+ if (eptr >= md->end_subject)
{
- SCHECK_PARTIAL();
+ SCHECK_PARTIAL();
break;
- }
+ }
GETCHARLEN(c, eptr, len);
if (prop_fail_result) break;
eptr+= len;
@@ -4398,11 +4398,11 @@ for (;;)
for (i = min; i < max; i++)
{
int len = 1;
- if (eptr >= md->end_subject)
+ if (eptr >= md->end_subject)
{
- SCHECK_PARTIAL();
+ SCHECK_PARTIAL();
break;
- }
+ }
GETCHARLEN(c, eptr, len);
prop_chartype = UCD_CHARTYPE(c);
if ((prop_chartype == ucp_Lu ||
@@ -4417,11 +4417,11 @@ for (;;)
for (i = min; i < max; i++)
{
int len = 1;
- if (eptr >= md->end_subject)
+ if (eptr >= md->end_subject)
{
- SCHECK_PARTIAL();
+ SCHECK_PARTIAL();
break;
- }
+ }
GETCHARLEN(c, eptr, len);
prop_category = UCD_CATEGORY(c);
if ((prop_category == prop_value) == prop_fail_result)
@@ -4434,11 +4434,11 @@ for (;;)
for (i = min; i < max; i++)
{
int len = 1;
- if (eptr >= md->end_subject)
+ if (eptr >= md->end_subject)
{
- SCHECK_PARTIAL();
+ SCHECK_PARTIAL();
break;
- }
+ }
GETCHARLEN(c, eptr, len);
prop_chartype = UCD_CHARTYPE(c);
if ((prop_chartype == prop_value) == prop_fail_result)
@@ -4451,11 +4451,11 @@ for (;;)
for (i = min; i < max; i++)
{
int len = 1;
- if (eptr >= md->end_subject)
+ if (eptr >= md->end_subject)
{
- SCHECK_PARTIAL();
+ SCHECK_PARTIAL();
break;
- }
+ }
GETCHARLEN(c, eptr, len);
prop_script = UCD_SCRIPT(c);
if ((prop_script == prop_value) == prop_fail_result)
@@ -4484,11 +4484,11 @@ for (;;)
{
for (i = min; i < max; i++)
{
- if (eptr >= md->end_subject)
+ if (eptr >= md->end_subject)
{
- SCHECK_PARTIAL();
+ SCHECK_PARTIAL();
break;
- }
+ }
GETCHARINCTEST(c, eptr);
prop_category = UCD_CATEGORY(c);
if (prop_category == ucp_M) break;
@@ -4544,11 +4544,11 @@ for (;;)
{
for (i = min; i < max; i++)
{
- if (eptr >= md->end_subject)
+ if (eptr >= md->end_subject)
{
- SCHECK_PARTIAL();
+ SCHECK_PARTIAL();
break;
- }
+ }
if (IS_NEWLINE(eptr)) break;
eptr++;
while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++;
@@ -4561,11 +4561,11 @@ for (;;)
{
for (i = min; i < max; i++)
{
- if (eptr >= md->end_subject)
+ if (eptr >= md->end_subject)
{
- SCHECK_PARTIAL();
+ SCHECK_PARTIAL();
break;
- }
+ }
if (IS_NEWLINE(eptr)) break;
eptr++;
while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++;
@@ -4578,11 +4578,11 @@ for (;;)
{
for (i = min; i < max; i++)
{
- if (eptr >= md->end_subject)
+ if (eptr >= md->end_subject)
{
- SCHECK_PARTIAL();
+ SCHECK_PARTIAL();
break;
- }
+ }
eptr++;
while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++;
}
@@ -4598,7 +4598,7 @@ for (;;)
{
eptr = md->end_subject;
SCHECK_PARTIAL();
- }
+ }
else eptr += c;
break;
@@ -4606,11 +4606,11 @@ for (;;)
for (i = min; i < max; i++)
{
int len = 1;
- if (eptr >= md->end_subject)
+ if (eptr >= md->end_subject)
{
- SCHECK_PARTIAL();
+ SCHECK_PARTIAL();
break;
- }
+ }
GETCHARLEN(c, eptr, len);
if (c == 0x000d)
{
@@ -4635,11 +4635,11 @@ for (;;)
{
BOOL gotspace;
int len = 1;
- if (eptr >= md->end_subject)
+ if (eptr >= md->end_subject)
{
- SCHECK_PARTIAL();
+ SCHECK_PARTIAL();
break;
- }
+ }
GETCHARLEN(c, eptr, len);
switch(c)
{
@@ -4677,11 +4677,11 @@ for (;;)
{
BOOL gotspace;
int len = 1;
- if (eptr >= md->end_subject)
+ if (eptr >= md->end_subject)
{
- SCHECK_PARTIAL();
+ SCHECK_PARTIAL();
break;
- }
+ }
GETCHARLEN(c, eptr, len);
switch(c)
{
@@ -4705,11 +4705,11 @@ for (;;)
for (i = min; i < max; i++)
{
int len = 1;
- if (eptr >= md->end_subject)
+ if (eptr >= md->end_subject)
{
- SCHECK_PARTIAL();
+ SCHECK_PARTIAL();
break;
- }
+ }
GETCHARLEN(c, eptr, len);
if (c < 256 && (md->ctypes[c] & ctype_digit) != 0) break;
eptr+= len;
@@ -4720,11 +4720,11 @@ for (;;)
for (i = min; i < max; i++)
{
int len = 1;
- if (eptr >= md->end_subject)
+ if (eptr >= md->end_subject)
{
- SCHECK_PARTIAL();
+ SCHECK_PARTIAL();
break;
- }
+ }
GETCHARLEN(c, eptr, len);
if (c >= 256 ||(md->ctypes[c] & ctype_digit) == 0) break;
eptr+= len;
@@ -4735,11 +4735,11 @@ for (;;)
for (i = min; i < max; i++)
{
int len = 1;
- if (eptr >= md->end_subject)
+ if (eptr >= md->end_subject)
{
- SCHECK_PARTIAL();
+ SCHECK_PARTIAL();
break;
- }
+ }
GETCHARLEN(c, eptr, len);
if (c < 256 && (md->ctypes[c] & ctype_space) != 0) break;
eptr+= len;
@@ -4750,11 +4750,11 @@ for (;;)
for (i = min; i < max; i++)
{
int len = 1;
- if (eptr >= md->end_subject)
+ if (eptr >= md->end_subject)
{
- SCHECK_PARTIAL();
+ SCHECK_PARTIAL();
break;
- }
+ }
GETCHARLEN(c, eptr, len);
if (c >= 256 ||(md->ctypes[c] & ctype_space) == 0) break;
eptr+= len;
@@ -4765,11 +4765,11 @@ for (;;)
for (i = min; i < max; i++)
{
int len = 1;
- if (eptr >= md->end_subject)
+ if (eptr >= md->end_subject)
{
- SCHECK_PARTIAL();
+ SCHECK_PARTIAL();
break;
- }
+ }
GETCHARLEN(c, eptr, len);
if (c < 256 && (md->ctypes[c] & ctype_word) != 0) break;
eptr+= len;
@@ -4780,11 +4780,11 @@ for (;;)
for (i = min; i < max; i++)
{
int len = 1;
- if (eptr >= md->end_subject)
+ if (eptr >= md->end_subject)
{
- SCHECK_PARTIAL();
+ SCHECK_PARTIAL();
break;
- }
+ }
GETCHARLEN(c, eptr, len);
if (c >= 256 || (md->ctypes[c] & ctype_word) == 0) break;
eptr+= len;
@@ -4816,11 +4816,11 @@ for (;;)
case OP_ANY:
for (i = min; i < max; i++)
{
- if (eptr >= md->end_subject)
+ if (eptr >= md->end_subject)
{
SCHECK_PARTIAL();
break;
- }
+ }
if (IS_NEWLINE(eptr)) break;
eptr++;
}
@@ -4833,18 +4833,18 @@ for (;;)
{
eptr = md->end_subject;
SCHECK_PARTIAL();
- }
+ }
else eptr += c;
break;
case OP_ANYNL:
for (i = min; i < max; i++)
{
- if (eptr >= md->end_subject)
+ if (eptr >= md->end_subject)
{
- SCHECK_PARTIAL();
+ SCHECK_PARTIAL();
break;
- }
+ }
c = *eptr;
if (c == 0x000d)
{
@@ -4865,11 +4865,11 @@ for (;;)
case OP_NOT_HSPACE:
for (i = min; i < max; i++)
{
- if (eptr >= md->end_subject)
+ if (eptr >= md->end_subject)
{
- SCHECK_PARTIAL();
+ SCHECK_PARTIAL();
break;
- }
+ }
c = *eptr;
if (c == 0x09 || c == 0x20 || c == 0xa0) break;
eptr++;
@@ -4879,11 +4879,11 @@ for (;;)
case OP_HSPACE:
for (i = min; i < max; i++)
{
- if (eptr >= md->end_subject)
+ if (eptr >= md->end_subject)
{
- SCHECK_PARTIAL();
+ SCHECK_PARTIAL();
break;
- }
+ }
c = *eptr;
if (c != 0x09 && c != 0x20 && c != 0xa0) break;
eptr++;
@@ -4893,11 +4893,11 @@ for (;;)
case OP_NOT_VSPACE:
for (i = min; i < max; i++)
{
- if (eptr >= md->end_subject)
+ if (eptr >= md->end_subject)
{
- SCHECK_PARTIAL();
+ SCHECK_PARTIAL();
break;
- }
+ }
c = *eptr;
if (c == 0x0a || c == 0x0b || c == 0x0c || c == 0x0d || c == 0x85)
break;
@@ -4908,11 +4908,11 @@ for (;;)
case OP_VSPACE:
for (i = min; i < max; i++)
{
- if (eptr >= md->end_subject)
+ if (eptr >= md->end_subject)
{
- SCHECK_PARTIAL();
+ SCHECK_PARTIAL();
break;
- }
+ }
c = *eptr;
if (c != 0x0a && c != 0x0b && c != 0x0c && c != 0x0d && c != 0x85)
break;
@@ -4923,11 +4923,11 @@ for (;;)
case OP_NOT_DIGIT:
for (i = min; i < max; i++)
{
- if (eptr >= md->end_subject)
+ if (eptr >= md->end_subject)
{
- SCHECK_PARTIAL();
+ SCHECK_PARTIAL();
break;
- }
+ }
if ((md->ctypes[*eptr] & ctype_digit) != 0) break;
eptr++;
}
@@ -4936,11 +4936,11 @@ for (;;)
case OP_DIGIT:
for (i = min; i < max; i++)
{
- if (eptr >= md->end_subject)
+ if (eptr >= md->end_subject)
{
- SCHECK_PARTIAL();
+ SCHECK_PARTIAL();
break;
- }
+ }
if ((md->ctypes[*eptr] & ctype_digit) == 0) break;
eptr++;
}
@@ -4949,11 +4949,11 @@ for (;;)
case OP_NOT_WHITESPACE:
for (i = min; i < max; i++)
{
- if (eptr >= md->end_subject)
+ if (eptr >= md->end_subject)
{
- SCHECK_PARTIAL();
+ SCHECK_PARTIAL();
break;
- }
+ }
if ((md->ctypes[*eptr] & ctype_space) != 0) break;
eptr++;
}
@@ -4962,11 +4962,11 @@ for (;;)
case OP_WHITESPACE:
for (i = min; i < max; i++)
{
- if (eptr >= md->end_subject)
+ if (eptr >= md->end_subject)
{
- SCHECK_PARTIAL();
+ SCHECK_PARTIAL();
break;
- }
+ }
if ((md->ctypes[*eptr] & ctype_space) == 0) break;
eptr++;
}
@@ -4975,11 +4975,11 @@ for (;;)
case OP_NOT_WORDCHAR:
for (i = min; i < max; i++)
{
- if (eptr >= md->end_subject)
+ if (eptr >= md->end_subject)
{
- SCHECK_PARTIAL();
+ SCHECK_PARTIAL();
break;
- }
+ }
if ((md->ctypes[*eptr] & ctype_word) != 0) break;
eptr++;
}
@@ -4988,11 +4988,11 @@ for (;;)
case OP_WORDCHAR:
for (i = min; i < max; i++)
{
- if (eptr >= md->end_subject)
+ if (eptr >= md->end_subject)
{
- SCHECK_PARTIAL();
+ SCHECK_PARTIAL();
break;
- }
+ }
if ((md->ctypes[*eptr] & ctype_word) == 0) break;
eptr++;
}
diff --git a/testdata/testinput7 b/testdata/testinput7
index dbc66bb..2b42d10 100644
--- a/testdata/testinput7
+++ b/testdata/testinput7
@@ -4507,9 +4507,12 @@
thejk;adlfj aenjl;fda asdfasd ehj;kjxyasiupd
\Ythejk;adlfj aenjl;fda asdfasd ehj;kjxyasiupd
-/abcd*/
+/abcd*/+
xxxxabcd\P
xxxxabcd\P\P
+ dddxxx\R
+ xxxxabcd\P\P
+ xxx\R
/abcd*/i
xxxxabcd\P
diff --git a/testdata/testoutput7 b/testdata/testoutput7
index 524450e..c1b4fd0 100644
--- a/testdata/testoutput7
+++ b/testdata/testoutput7
@@ -7514,12 +7514,24 @@ No match
\Ythejk;adlfj aenjl;fda asdfasd ehj;kjxyasiupd
No match
-/abcd*/
+/abcd*/+
xxxxabcd\P
0: abcd
+ 0+
1: abc
xxxxabcd\P\P
Partial match: abcd
+ dddxxx\R
+ 0: ddd
+ 0+ xxx
+ 1: dd
+ 2: d
+ 3:
+ xxxxabcd\P\P
+Partial match: abcd
+ xxx\R
+ 0:
+ 0+ xxx
/abcd*/i
xxxxabcd\P