#ifdef DEBUGGING
/* At least one required character in the target string is expressible only in
* UTF-8. */
-static const char* const non_utf8_target_but_utf8_required
+static const char non_utf8_target_but_utf8_required[]
= "Can't match, because target string needs to be in UTF-8\n";
#endif
: (U8*)(pos + off))
#define HOP4c(pos,off,llim, rlim) ((char*)HOP4(pos,off,llim, rlim))
-#define NEXTCHR_EOS -10 /* nextchr has fallen off the end */
-#define NEXTCHR_IS_EOS (nextchr < 0)
-
-#define SET_nextchr __ASSERT_(locinput <= reginfo->strend) \
- nextchr = ((locinput < reginfo->strend) ? UCHARAT(locinput) : NEXTCHR_EOS)
-
-#define SET_locinput(p) \
- locinput = (p); \
- SET_nextchr
-
#define PLACEHOLDER /* Something for the preprocessor to grab onto */
/* TODO: Combine JUMPABLE and HAS_TEXT to cache OP(rn) */
/* now look for the 'other' substring if defined */
- if (utf8_target ? prog->substrs->data[other_ix].utf8_substr
- : prog->substrs->data[other_ix].substr)
+ if (prog->substrs->data[other_ix].utf8_substr
+ || prog->substrs->data[other_ix].substr)
{
/* Take into account the "other" substring. */
char *last, *last1;
do_other_substr:
other = &prog->substrs->data[other_ix];
+ if (!utf8_target && !other->substr) {
+ if (!to_byte_substr(prog)) {
+ NON_UTF8_TARGET_BUT_UTF8_REQUIRED(fail);
+ }
+ }
/* if "other" is anchored:
* we've previously found a floating substr starting at check_at.
} else { \
uvc = _toFOLD_utf8_flags( (const U8*) uc, uc_end, foldbuf, &foldlen, \
flags); \
- len = UTF8SKIP(uc); \
+ len = UTF8_SAFE_SKIP(uc, uc_end); \
skiplen = UVCHR_SKIP( uvc ); \
foldlen -= skiplen; \
uscan = foldbuf + skiplen; \
case trie_utf8l: \
_CHECK_AND_WARN_PROBLEMATIC_LOCALE; \
if (utf8_target && UTF8_IS_ABOVE_LATIN1(*uc)) { \
- _CHECK_AND_OUTPUT_WIDE_LOCALE_UTF8_MSG(uc, uc + UTF8SKIP(uc)); \
+ _CHECK_AND_OUTPUT_WIDE_LOCALE_UTF8_MSG(uc, uc_end); \
} \
/* FALLTHROUGH */ \
case trie_utf8: \
STMT_START { \
while (s < strend) { \
CODE \
- s += ((UTF8) ? UTF8SKIP(s) : 1); \
+ s += ((UTF8) \
+ ? UTF8_SAFE_SKIP(s, reginfo->strend) \
+ : 1); \
} \
} STMT_END
#define REXEC_FBC_CLASS_SCAN_GUTS(UTF8, COND) \
if (COND) { \
FBC_CHECK_AND_TRY \
- s += ((UTF8) ? UTF8SKIP(s) : 1); \
+ s += ((UTF8) ? UTF8_SAFE_SKIP(s, reginfo->strend) : 1);\
previous_occurrence_end = s; \
} \
else { \
* of the one we're looking for. Knowing that, we can see right away if the
* next occurrence is adjacent to the previous. When 'doevery' is FALSE, we
* don't accept the 2nd and succeeding adjacent occurrences */
-#define FBC_CHECK_AND_TRY \
- if ( ( doevery \
- || s != previous_occurrence_end) \
- && (reginfo->intuit || regtry(reginfo, &s))) \
- { \
- goto got_it; \
+#define FBC_CHECK_AND_TRY \
+ if ( ( doevery \
+ || s != previous_occurrence_end) \
+ && ( reginfo->intuit \
+ || (s <= reginfo->strend && regtry(reginfo, &s)))) \
+ { \
+ goto got_it; \
}
previous_occurrence_end = s; \
}
+/* This differs from the above macros in that it is passed a single byte that
+ * is known to begin the next occurrence of the thing being looked for in 's'.
+ * It does a memchr to find the next occurrence of 'byte', before trying 'COND'
+ * at that position. */
+#define REXEC_FBC_FIND_NEXT_UTF8_BYTE_SCAN(byte, COND) \
+ while (s < strend) { \
+ s = (char *) memchr(s, byte, strend -s); \
+ if (s == NULL) { \
+ s = (char *) strend; \
+ break; \
+ } \
+ \
+ if (COND) { \
+ FBC_CHECK_AND_TRY \
+ s += UTF8_SAFE_SKIP(s, reginfo->strend); \
+ previous_occurrence_end = s; \
+ } \
+ else { \
+ s += UTF8SKIP(s); \
+ } \
+ }
+
/* The three macros below are slightly different versions of the same logic.
*
* The first is for /a and /aa when the target string is UTF-8. This can only
}
/* This is the macro to use when we want to see if something that looks like it
- * could match, actually does, and if so exits the loop */
-#define REXEC_FBC_TRYIT \
- if ((reginfo->intuit || regtry(reginfo, &s))) \
+ * could match, actually does, and if so exits the loop. It needs to be used
+ * only for bounds checking macros, as it allows for matching beyond the end of
+ * string (which should be zero length without having to look at the string
+ * contents) */
+#define REXEC_FBC_TRYIT \
+ if (reginfo->intuit || (s <= reginfo->strend && regtry(reginfo, &s))) \
goto got_it
/* The only difference between the BOUND and NBOUND cases is that
break;
case ANYOFM: /* ARG() is the base byte; FLAGS() the mask byte */
- /* UTF-8ness doesn't matter, so use 0 */
+ /* UTF-8ness doesn't matter because only matches UTF-8 invariants, so
+ * use 0 */
REXEC_FBC_FIND_NEXT_SCAN(0,
(char *) find_next_masked((U8 *) s, (U8 *) strend,
(U8) ARG(c), FLAGS(c)));
break;
- case NANYOFM:
- REXEC_FBC_FIND_NEXT_SCAN(0,
+ case NANYOFM: /* UTF-8ness does matter because can match UTF-8 variants.
+ */
+ REXEC_FBC_FIND_NEXT_SCAN(utf8_target,
(char *) find_span_end_mask((U8 *) s, (U8 *) strend,
(U8) ARG(c), FLAGS(c)));
break;
case ANYOFH:
- if (utf8_target) REXEC_FBC_CLASS_SCAN(TRUE,
+ if (utf8_target) { /* Can't possibly match a non-UTF-8 target */
+ REXEC_FBC_CLASS_SCAN(TRUE,
+ ( (U8) NATIVE_UTF8_TO_I8(*s) >= ANYOF_FLAGS(c)
+ && reginclass(prog, c, (U8*)s, (U8*) strend, utf8_target)));
+ }
+ break;
+
+ case ANYOFHb:
+ if (utf8_target) { /* Can't possibly match a non-UTF-8 target */
+
+ /* We know what the first byte of any matched string should be */
+ U8 first_byte = FLAGS(c);
+
+ REXEC_FBC_FIND_NEXT_UTF8_BYTE_SCAN(first_byte,
reginclass(prog, c, (U8*)s, (U8*) strend, utf8_target));
+ }
+ break;
+
+ case ANYOFHr:
+ if (utf8_target) { /* Can't possibly match a non-UTF-8 target */
+ REXEC_FBC_CLASS_SCAN(TRUE,
+ ( inRANGE((U8) NATIVE_UTF8_TO_I8(*s),
+ LOWEST_ANYOF_HRx_BYTE(ANYOF_FLAGS(c)),
+ HIGHEST_ANYOF_HRx_BYTE(ANYOF_FLAGS(c)))
+ && reginclass(prog, c, (U8*)s, (U8*) strend, utf8_target)));
+ }
break;
case EXACTFAA_NO_TRIE: /* This node only generated for non-utf8 patterns */
{
goto got_it;
}
- s += (utf8_target) ? UTF8SKIP(s) : 1;
+ s += (utf8_target) ? UTF8_SAFE_SKIP(s, reginfo->strend) : 1;
}
break;
}
}
/* Didn't match. Try at the next position (if there is one) */
- s += (utf8_target) ? UTF8SKIP(s) : 1;
+ s += (utf8_target) ? UTF8_SAFE_SKIP(s, reginfo->strend) : 1;
if (UNLIKELY(s >= reginfo->strend)) {
break;
}
goto got_it;
}
before = after;
- s += UTF8SKIP(s);
+ s += UTF8_SAFE_SKIP(s, reginfo->strend);
}
}
else { /* Not utf8. Everything is a GCB except between CR and
/* And, since this is a bound, it can match after the final
* character in the string */
- if ((reginfo->intuit || regtry(reginfo, &s))) {
+ if ( reginfo->intuit
+ || (s <= reginfo->strend && regtry(reginfo, &s)))
+ {
goto got_it;
}
break;
if (reginfo->intuit || regtry(reginfo, &s)) {
goto got_it;
}
- s += (utf8_target) ? UTF8SKIP(s) : 1;
+ s += (utf8_target) ? UTF8_SAFE_SKIP(s, reginfo->strend) : 1;
if (UNLIKELY(s >= reginfo->strend)) {
break;
}
goto got_it;
}
before = after;
- s += UTF8SKIP(s);
+ s += UTF8_SAFE_SKIP(s, reginfo->strend);
}
}
else { /* Not utf8. */
}
}
- if (reginfo->intuit || regtry(reginfo, &s)) {
+ if ( reginfo->intuit
+ || (s <= reginfo->strend && regtry(reginfo, &s)))
+ {
goto got_it;
}
if (reginfo->intuit || regtry(reginfo, &s)) {
goto got_it;
}
- s += (utf8_target) ? UTF8SKIP(s) : 1;
+ s += (utf8_target) ? UTF8_SAFE_SKIP(s, reginfo->strend) : 1;
if (UNLIKELY(s >= reginfo->strend)) {
break;
}
goto got_it;
}
before = after;
- s += UTF8SKIP(s);
+ s += UTF8_SAFE_SKIP(s, reginfo->strend);
}
}
else { /* Not utf8. */
/* Here are at the final position in the target string. The SB
* value is always true here, so matches, depending on other
* constraints */
- if (reginfo->intuit || regtry(reginfo, &s)) {
+ if ( reginfo->intuit
+ || (s <= reginfo->strend && regtry(reginfo, &s)))
+ {
goto got_it;
}
if (reginfo->intuit || regtry(reginfo, &s)) {
goto got_it;
}
- s += (utf8_target) ? UTF8SKIP(s) : 1;
+ s += (utf8_target) ? UTF8_SAFE_SKIP(s, reginfo->strend) : 1;
if (UNLIKELY(s >= reginfo->strend)) {
break;
}
}
previous = before;
before = after;
- s += UTF8SKIP(s);
+ s += UTF8_SAFE_SKIP(s, reginfo->strend);
}
}
else { /* Not utf8. */
}
}
- if (reginfo->intuit || regtry(reginfo, &s)) {
+ if ( reginfo->intuit
+ || (s <= reginfo->strend && regtry(reginfo, &s)))
+ {
goto got_it;
}
}
LEAVE;
goto got_it;
}
- s = HOPc(s,1);
+ if (s < reginfo->strend) {
+ s = HOPc(s,1);
+ }
DEBUG_TRIE_EXECUTE_r({
Perl_re_printf( aTHX_ "Pattern failed. Looking for new start point...\n");
});
RXp_MATCH_UTF8_set(prog, utf8_target);
prog->offs[0].start = s - strbeg;
prog->offs[0].end = utf8_target
- ? (char*)utf8_hop((U8*)s, prog->minlenret) - strbeg
+ ? (char*)utf8_hop_forward((U8*)s, prog->minlenret, (U8 *) strend) - strbeg
: s - strbeg + prog->minlenret;
if ( !(flags & REXEC_NOT_FIRST) )
S_reg_set_capture_string(aTHX_ rx,
if (*s == ch) {
DEBUG_EXECUTE_r( did_match = 1 );
if (regtry(reginfo, &s)) goto got_it;
- s += UTF8SKIP(s);
+ s += UTF8_SAFE_SKIP(s, strend);
while (s < strend && *s == ch)
s += UTF8SKIP(s);
}
return 0;
}
-
-#define sayYES goto yes
-#define sayNO goto no
-#define sayNO_SILENT goto no_silent
-
-/* we dont use STMT_START/END here because it leads to
- "unreachable code" warnings, which are bogus, but distracting. */
-#define CACHEsayNO \
- if (ST.cache_mask) \
- reginfo->info_aux->poscache[ST.cache_offset] |= ST.cache_mask; \
- sayNO
-
/* this is used to determine how far from the left messages like
'failed...' are printed in regexec.c. It should be set such that
messages are inline with the regop output that created them.
}
#endif /* DEBUGGING */
-
-#define CHRTEST_UNINIT -1001 /* c1/c2 haven't been calculated yet */
-#define CHRTEST_VOID -1000 /* the c1/c2 "next char" test should be skipped */
-#define CHRTEST_NOT_A_CP_1 -999
-#define CHRTEST_NOT_A_CP_2 -998
-
/* grab a new slab and return the first slot in it */
STATIC regmatch_state *
return SLAB_FIRST(s);
}
-
-/* push a new state then goto it */
-
-#define PUSH_STATE_GOTO(state, node, input) \
- pushinput = input; \
- scan = node; \
- st->resume_state = state; \
- goto push_state;
-
-/* push a new state with success backtracking, then goto it */
-
-#define PUSH_YES_STATE_GOTO(state, node, input) \
- pushinput = input; \
- scan = node; \
- st->resume_state = state; \
- goto push_yes_state;
-
-
-
-
-/*
-
-regmatch() - main matching routine
-
-This is basically one big switch statement in a loop. We execute an op,
-set 'next' to point the next op, and continue. If we come to a point which
-we may need to backtrack to on failure such as (A|B|C), we push a
-backtrack state onto the backtrack stack. On failure, we pop the top
-state, and re-enter the loop at the state indicated. If there are no more
-states to pop, we return failure.
-
-Sometimes we also need to backtrack on success; for example /A+/, where
-after successfully matching one A, we need to go back and try to
-match another one; similarly for lookahead assertions: if the assertion
-completes successfully, we backtrack to the state just before the assertion
-and then carry on. In these cases, the pushed state is marked as
-'backtrack on success too'. This marking is in fact done by a chain of
-pointers, each pointing to the previous 'yes' state. On success, we pop to
-the nearest yes state, discarding any intermediate failure-only states.
-Sometimes a yes state is pushed just to force some cleanup code to be
-called at the end of a successful match or submatch; e.g. (??{$re}) uses
-it to free the inner regex.
-
-Note that failure backtracking rewinds the cursor position, while
-success backtracking leaves it alone.
-
-A pattern is complete when the END op is executed, while a subpattern
-such as (?=foo) is complete when the SUCCESS op is executed. Both of these
-ops trigger the "pop to last yes state if any, otherwise return true"
-behaviour.
-
-A common convention in this function is to use A and B to refer to the two
-subpatterns (or to the first nodes thereof) in patterns like /A*B/: so A is
-the subpattern to be matched possibly multiple times, while B is the entire
-rest of the pattern. Variable and state names reflect this convention.
-
-The states in the main switch are the union of ops and failure/success of
-substates associated with with that op. For example, IFMATCH is the op
-that does lookahead assertions /(?=A)B/ and so the IFMATCH state means
-'execute IFMATCH'; while IFMATCH_A is a state saying that we have just
-successfully matched A and IFMATCH_A_fail is a state saying that we have
-just failed to match A. Resume states always come in pairs. The backtrack
-state we push is marked as 'IFMATCH_A', but when that is popped, we resume
-at IFMATCH_A or IFMATCH_A_fail, depending on whether we are backtracking
-on success or failure.
-
-The struct that holds a backtracking state is actually a big union, with
-one variant for each major type of op. The variable st points to the
-top-most backtrack struct. To make the code clearer, within each
-block of code we #define ST to alias the relevant union.
-
-Here's a concrete example of a (vastly oversimplified) IFMATCH
-implementation:
-
- switch (state) {
- ....
-
-#define ST st->u.ifmatch
-
- case IFMATCH: // we are executing the IFMATCH op, (?=A)B
- ST.foo = ...; // some state we wish to save
- ...
- // push a yes backtrack state with a resume value of
- // IFMATCH_A/IFMATCH_A_fail, then continue execution at the
- // first node of A:
- PUSH_YES_STATE_GOTO(IFMATCH_A, A, newinput);
- // NOTREACHED
-
- case IFMATCH_A: // we have successfully executed A; now continue with B
- next = B;
- bar = ST.foo; // do something with the preserved value
- break;
-
- case IFMATCH_A_fail: // A failed, so the assertion failed
- ...; // do some housekeeping, then ...
- sayNO; // propagate the failure
-
-#undef ST
-
- ...
- }
-
-For any old-timers reading this who are familiar with the old recursive
-approach, the code above is equivalent to:
-
- case IFMATCH: // we are executing the IFMATCH op, (?=A)B
- {
- int foo = ...
- ...
- if (regmatch(A)) {
- next = B;
- bar = foo;
- break;
- }
- ...; // do some housekeeping, then ...
- sayNO; // propagate the failure
- }
-
-The topmost backtrack state, pointed to by st, is usually free. If you
-want to claim it, populate any ST.foo fields in it with values you wish to
-save, then do one of
-
- PUSH_STATE_GOTO(resume_state, node, newinput);
- PUSH_YES_STATE_GOTO(resume_state, node, newinput);
-
-which sets that backtrack state's resume value to 'resume_state', pushes a
-new free entry to the top of the backtrack stack, then goes to 'node'.
-On backtracking, the free slot is popped, and the saved state becomes the
-new free state. An ST.foo field in this new top state can be temporarily
-accessed to retrieve values, but once the main loop is re-entered, it
-becomes available for reuse.
-
-Note that the depth of the backtrack stack constantly increases during the
-left-to-right execution of the pattern, rather than going up and down with
-the pattern nesting. For example the stack is at its maximum at Z at the
-end of the pattern, rather than at X in the following:
-
- /(((X)+)+)+....(Y)+....Z/
-
-The only exceptions to this are lookahead/behind assertions and the cut,
-(?>A), which pop all the backtrack states associated with A before
-continuing.
-
-Backtrack state structs are allocated in slabs of about 4K in size.
-PL_regmatch_state and st always point to the currently active state,
-and PL_regmatch_slab points to the slab currently containing
-PL_regmatch_state. The first time regmatch() is called, the first slab is
-allocated, and is never freed until interpreter destruction. When the slab
-is full, a new one is allocated and chained to the end. At exit from
-regmatch(), slabs allocated since entry are freed.
-
-*/
-
-
-#define DEBUG_STATE_pp(pp) \
- DEBUG_STATE_r({ \
- DUMP_EXEC_POS(locinput, scan, utf8_target,depth); \
- Perl_re_printf( aTHX_ \
- "%*s" pp " %s%s%s%s%s\n", \
- INDENT_CHARS(depth), "", \
- PL_reg_name[st->resume_state], \
- ((st==yes_state||st==mark_state) ? "[" : ""), \
- ((st==yes_state) ? "Y" : ""), \
- ((st==mark_state) ? "M" : ""), \
- ((st==yes_state||st==mark_state) ? "]" : "") \
- ); \
- });
-
-
-#define REG_NODE_NUM(x) ((x) ? (int)((x)-prog) : -1)
-
#ifdef DEBUGGING
STATIC void
return 0;
}
+#define CHRTEST_UNINIT -1001 /* c1/c2 haven't been calculated yet */
+#define CHRTEST_VOID -1000 /* the c1/c2 "next char" test should be skipped */
+#define CHRTEST_NOT_A_CP_1 -999
+#define CHRTEST_NOT_A_CP_2 -998
+
static bool
S_setup_EXACTISH_ST_c1_c2(pTHX_ const regnode * const text_node, int *c1p,
U8* c1_utf8, int *c2p, U8* c2_utf8, regmatch_info *reginfo)
return wb;
}
+/* Macros for regmatch(), using its internal variables */
+#define NEXTCHR_EOS -10 /* nextchr has fallen off the end */
+#define NEXTCHR_IS_EOS (nextchr < 0)
+
+#define SET_nextchr \
+ nextchr = ((locinput < reginfo->strend) ? UCHARAT(locinput) : NEXTCHR_EOS)
+
+#define SET_locinput(p) \
+ locinput = (p); \
+ SET_nextchr
+
+#define sayYES goto yes
+#define sayNO goto no
+#define sayNO_SILENT goto no_silent
+
+/* we dont use STMT_START/END here because it leads to
+ "unreachable code" warnings, which are bogus, but distracting. */
+#define CACHEsayNO \
+ if (ST.cache_mask) \
+ reginfo->info_aux->poscache[ST.cache_offset] |= ST.cache_mask; \
+ sayNO
+
#define EVAL_CLOSE_PAREN_IS(st,expr) \
( \
( ( st ) ) && \
#define EVAL_CLOSE_PAREN_CLEAR(st) \
(st)->u.eval.close_paren = 0
+/* push a new state then goto it */
+
+#define PUSH_STATE_GOTO(state, node, input, eol, sr0) \
+ pushinput = input; \
+ pusheol = eol; \
+ pushsr0 = sr0; \
+ scan = node; \
+ st->resume_state = state; \
+ goto push_state;
+
+/* push a new state with success backtracking, then goto it */
+
+#define PUSH_YES_STATE_GOTO(state, node, input, eol, sr0) \
+ pushinput = input; \
+ pusheol = eol; \
+ pushsr0 = sr0; \
+ scan = node; \
+ st->resume_state = state; \
+ goto push_yes_state;
+
+#define DEBUG_STATE_pp(pp) \
+ DEBUG_STATE_r({ \
+ DUMP_EXEC_POS(locinput, scan, utf8_target,depth); \
+ Perl_re_printf( aTHX_ \
+ "%*s" pp " %s%s%s%s%s\n", \
+ INDENT_CHARS(depth), "", \
+ PL_reg_name[st->resume_state], \
+ ((st==yes_state||st==mark_state) ? "[" : ""), \
+ ((st==yes_state) ? "Y" : ""), \
+ ((st==mark_state) ? "M" : ""), \
+ ((st==yes_state||st==mark_state) ? "]" : "") \
+ ); \
+ });
+
+/*
+
+regmatch() - main matching routine
+
+This is basically one big switch statement in a loop. We execute an op,
+set 'next' to point the next op, and continue. If we come to a point which
+we may need to backtrack to on failure such as (A|B|C), we push a
+backtrack state onto the backtrack stack. On failure, we pop the top
+state, and re-enter the loop at the state indicated. If there are no more
+states to pop, we return failure.
+
+Sometimes we also need to backtrack on success; for example /A+/, where
+after successfully matching one A, we need to go back and try to
+match another one; similarly for lookahead assertions: if the assertion
+completes successfully, we backtrack to the state just before the assertion
+and then carry on. In these cases, the pushed state is marked as
+'backtrack on success too'. This marking is in fact done by a chain of
+pointers, each pointing to the previous 'yes' state. On success, we pop to
+the nearest yes state, discarding any intermediate failure-only states.
+Sometimes a yes state is pushed just to force some cleanup code to be
+called at the end of a successful match or submatch; e.g. (??{$re}) uses
+it to free the inner regex.
+
+Note that failure backtracking rewinds the cursor position, while
+success backtracking leaves it alone.
+
+A pattern is complete when the END op is executed, while a subpattern
+such as (?=foo) is complete when the SUCCESS op is executed. Both of these
+ops trigger the "pop to last yes state if any, otherwise return true"
+behaviour.
+
+A common convention in this function is to use A and B to refer to the two
+subpatterns (or to the first nodes thereof) in patterns like /A*B/: so A is
+the subpattern to be matched possibly multiple times, while B is the entire
+rest of the pattern. Variable and state names reflect this convention.
+
+The states in the main switch are the union of ops and failure/success of
+substates associated with with that op. For example, IFMATCH is the op
+that does lookahead assertions /(?=A)B/ and so the IFMATCH state means
+'execute IFMATCH'; while IFMATCH_A is a state saying that we have just
+successfully matched A and IFMATCH_A_fail is a state saying that we have
+just failed to match A. Resume states always come in pairs. The backtrack
+state we push is marked as 'IFMATCH_A', but when that is popped, we resume
+at IFMATCH_A or IFMATCH_A_fail, depending on whether we are backtracking
+on success or failure.
+
+The struct that holds a backtracking state is actually a big union, with
+one variant for each major type of op. The variable st points to the
+top-most backtrack struct. To make the code clearer, within each
+block of code we #define ST to alias the relevant union.
+
+Here's a concrete example of a (vastly oversimplified) IFMATCH
+implementation:
+
+ switch (state) {
+ ....
+
+#define ST st->u.ifmatch
+
+ case IFMATCH: // we are executing the IFMATCH op, (?=A)B
+ ST.foo = ...; // some state we wish to save
+ ...
+ // push a yes backtrack state with a resume value of
+ // IFMATCH_A/IFMATCH_A_fail, then continue execution at the
+ // first node of A:
+ PUSH_YES_STATE_GOTO(IFMATCH_A, A, newinput);
+ // NOTREACHED
+
+ case IFMATCH_A: // we have successfully executed A; now continue with B
+ next = B;
+ bar = ST.foo; // do something with the preserved value
+ break;
+
+ case IFMATCH_A_fail: // A failed, so the assertion failed
+ ...; // do some housekeeping, then ...
+ sayNO; // propagate the failure
+
+#undef ST
+
+ ...
+ }
+
+For any old-timers reading this who are familiar with the old recursive
+approach, the code above is equivalent to:
+
+ case IFMATCH: // we are executing the IFMATCH op, (?=A)B
+ {
+ int foo = ...
+ ...
+ if (regmatch(A)) {
+ next = B;
+ bar = foo;
+ break;
+ }
+ ...; // do some housekeeping, then ...
+ sayNO; // propagate the failure
+ }
+
+The topmost backtrack state, pointed to by st, is usually free. If you
+want to claim it, populate any ST.foo fields in it with values you wish to
+save, then do one of
+
+ PUSH_STATE_GOTO(resume_state, node, newinput, new_eol);
+ PUSH_YES_STATE_GOTO(resume_state, node, newinput, new_eol);
+
+which sets that backtrack state's resume value to 'resume_state', pushes a
+new free entry to the top of the backtrack stack, then goes to 'node'.
+On backtracking, the free slot is popped, and the saved state becomes the
+new free state. An ST.foo field in this new top state can be temporarily
+accessed to retrieve values, but once the main loop is re-entered, it
+becomes available for reuse.
+
+Note that the depth of the backtrack stack constantly increases during the
+left-to-right execution of the pattern, rather than going up and down with
+the pattern nesting. For example the stack is at its maximum at Z at the
+end of the pattern, rather than at X in the following:
+
+ /(((X)+)+)+....(Y)+....Z/
+
+The only exceptions to this are lookahead/behind assertions and the cut,
+(?>A), which pop all the backtrack states associated with A before
+continuing.
+
+Backtrack state structs are allocated in slabs of about 4K in size.
+PL_regmatch_state and st always point to the currently active state,
+and PL_regmatch_slab points to the slab currently containing
+PL_regmatch_state. The first time regmatch() is called, the first slab is
+allocated, and is never freed until interpreter destruction. When the slab
+is full, a new one is allocated and chained to the end. At exit from
+regmatch(), slabs allocated since entry are freed.
+
+In order to work with variable length lookbehinds, an upper limit is placed on
+lookbehinds which is set to where the match position is at the end of where the
+lookbehind would get to. Nothing in the lookbehind should match above that,
+except we should be able to look beyond if for things like \b, which need the
+next character in the string to be able to determine if this is a boundary or
+not. We also can't match the end of string/line unless we are also at the end
+of the entire string, so NEXTCHR_IS_EOS remains the same, and for those OPs
+that match a width, we have to add a condition that they are within the legal
+bounds of our window into the string.
+
+*/
+
/* returns -1 on failure, $+[0] on success */
STATIC SSize_t
S_regmatch(pTHX_ regmatch_info *reginfo, char *startpos, regnode *prog)
SSize_t ln = 0; /* len or last; init to avoid compiler warning */
SSize_t endref = 0; /* offset of end of backref when ln is start */
char *locinput = startpos;
+ char *loceol = reginfo->strend;
char *pushinput; /* where to continue after a PUSH */
+ char *pusheol; /* where to stop matching (loceol) after a PUSH */
+ U8 *pushsr0; /* save starting pos of script run */
I32 nextchr; /* is always set to UCHARAT(locinput), or -1 at EOS */
bool result = 0; /* return value of S_regmatch */
/* update the startpoint */
st->u.keeper.val = rex->offs[0].start;
rex->offs[0].start = locinput - reginfo->strbeg;
- PUSH_STATE_GOTO(KEEPS_next, next, locinput);
+ PUSH_STATE_GOTO(KEEPS_next, next, locinput, loceol,
+ script_run_begin);
NOT_REACHED; /* NOTREACHED */
case KEEPS_next_fail:
break;
case SANY: /* /./s */
- if (NEXTCHR_IS_EOS)
+ if (NEXTCHR_IS_EOS || locinput >= loceol)
sayNO;
goto increment_locinput;
case REG_ANY: /* /./ */
- if ((NEXTCHR_IS_EOS) || nextchr == '\n')
+ if ( NEXTCHR_IS_EOS
+ || locinput >= loceol
+ || nextchr == '\n')
+ {
sayNO;
+ }
goto increment_locinput;
/* In this case the charclass data is available inline so
we can fail fast without a lot of extra overhead.
*/
- if(!NEXTCHR_IS_EOS && !ANYOF_BITMAP_TEST(scan, nextchr)) {
+ if ( ! NEXTCHR_IS_EOS
+ && locinput < loceol
+ && ! ANYOF_BITMAP_TEST(scan, nextchr))
+ {
DEBUG_EXECUTE_r(
Perl_re_exec_indentf( aTHX_ "%sTRIE: failed to match trie start class...%s\n",
depth, PL_colors[4], PL_colors[5])
}
}
if ( trie->bitmap
- && (NEXTCHR_IS_EOS || !TRIE_BITMAP_TEST(trie, nextchr)))
+ && ( NEXTCHR_IS_EOS
+ || locinput >= loceol
+ || ! TRIE_BITMAP_TEST(trie, nextchr)))
{
if (trie->states[ state ].wordnum) {
DEBUG_EXECUTE_r(
shortest accept state and the wordnum of the longest
accept state */
- while ( state && uc <= (U8*)(reginfo->strend) ) {
+ while ( state && uc <= (U8*)(loceol) ) {
U32 base = trie->states[ state ].trans.base;
UV uvc = 0;
U16 charid = 0;
});
/* read a char and goto next state */
- if ( base && (foldlen || uc < (U8*)(reginfo->strend))) {
+ if ( base && (foldlen || uc < (U8*)(loceol))) {
I32 offset;
REXEC_TRIE_READ_CHAR(trie_type, trie, widecharmap, uc,
- (U8 *) reginfo->strend, uscan,
+ (U8 *) loceol, uscan,
len, uvc, charid, foldlen,
foldbuf, uniflags);
charcount++;
while (chars) {
if (utf8_target) {
+ /* XXX This assumes the length is well-formed, as
+ * does the UTF8SKIP below */
uvc = utf8n_to_uvchr((U8*)uc, UTF8_MAXLEN, &len,
uniflags);
uc += len;
});
if ( ST.accepted > 1 || has_cutgroup || ST.jump ) {
- PUSH_STATE_GOTO(TRIE_next, scan, (char*)uc);
+ PUSH_STATE_GOTO(TRIE_next, scan, (char*)uc, loceol,
+ script_run_begin);
NOT_REACHED; /* NOTREACHED */
}
/* only one choice left - just continue */
* is an invariant, but there are tests in the test suite
* dealing with (??{...}) which violate this) */
while (s < e) {
- if (l >= reginfo->strend
+ if ( l >= loceol
|| UTF8_IS_ABOVE_LATIN1(* (U8*) l))
{
sayNO;
else {
/* The target is not utf8, the pattern is utf8. */
while (s < e) {
- if (l >= reginfo->strend
+ if ( l >= loceol
|| UTF8_IS_ABOVE_LATIN1(* (U8*) s))
{
sayNO;
else {
/* The target and the pattern have the same utf8ness. */
/* Inline the first character, for speed. */
- if (reginfo->strend - locinput < ln
+ if ( loceol - locinput < ln
|| UCHARAT(s) != nextchr
|| (ln > 1 && memNE(s, locinput, ln)))
{
/* Either target or the pattern are utf8, or has the issue where
* the fold lengths may differ. */
const char * const l = locinput;
- char *e = reginfo->strend;
+ char *e = loceol;
if (! foldEQ_utf8_flags(l, &e, 0, utf8_target,
s, 0, ln, is_utf8_pat,fold_utf8_flags))
{
sayNO;
}
- if (reginfo->strend - locinput < ln)
+ if (loceol - locinput < ln)
sayNO;
if (ln > 1 && ! folder(locinput, s, ln))
sayNO;
/* FALLTHROUGH */
case ANYOFD: /* /[abc]/d */
case ANYOF: /* /[abc]/ */
- if (NEXTCHR_IS_EOS)
+ if (NEXTCHR_IS_EOS || locinput >= loceol)
sayNO;
if ( (! utf8_target || UTF8_IS_INVARIANT(*locinput))
&& ! (ANYOF_FLAGS(scan) & ~ ANYOF_MATCHES_ALL_ABOVE_BITMAP))
locinput++;
}
else {
- if (!reginclass(rex, scan, (U8*)locinput, (U8*)reginfo->strend,
+ if (!reginclass(rex, scan, (U8*)locinput, (U8*) loceol,
utf8_target))
{
sayNO;
break;
case ANYOFM:
- if (NEXTCHR_IS_EOS || (UCHARAT(locinput) & FLAGS(scan)) != ARG(scan)) {
+ if ( NEXTCHR_IS_EOS
+ || (UCHARAT(locinput) & FLAGS(scan)) != ARG(scan)
+ || locinput >= loceol)
+ {
sayNO;
}
locinput++; /* ANYOFM is always single byte */
break;
case NANYOFM:
- if (NEXTCHR_IS_EOS || (UCHARAT(locinput) & FLAGS(scan)) == ARG(scan)) {
+ if ( NEXTCHR_IS_EOS
+ || (UCHARAT(locinput) & FLAGS(scan)) == ARG(scan)
+ || locinput >= loceol)
+ {
sayNO;
}
goto increment_locinput;
case ANYOFH:
if ( ! utf8_target
|| NEXTCHR_IS_EOS
- || ! reginclass(rex, scan, (U8*)locinput, (U8*)reginfo->strend,
+ || ANYOF_FLAGS(scan) > NATIVE_UTF8_TO_I8((U8) *locinput)
+ || ! reginclass(rex, scan, (U8*)locinput, (U8*) loceol,
+ utf8_target))
+ {
+ sayNO;
+ }
+ goto increment_locinput;
+ break;
+
+ case ANYOFHb:
+ if ( ! utf8_target
+ || NEXTCHR_IS_EOS
+ || ANYOF_FLAGS(scan) != (U8) *locinput
+ || ! reginclass(rex, scan, (U8*)locinput, (U8*) loceol,
+ utf8_target))
+ {
+ sayNO;
+ }
+ goto increment_locinput;
+ break;
+
+ case ANYOFHr:
+ if ( ! utf8_target
+ || NEXTCHR_IS_EOS
+ || ! inRANGE((U8) NATIVE_UTF8_TO_I8(*locinput),
+ LOWEST_ANYOF_HRx_BYTE(ANYOF_FLAGS(scan)),
+ HIGHEST_ANYOF_HRx_BYTE(ANYOF_FLAGS(scan)))
+ || ! reginclass(rex, scan, (U8*)locinput, (U8*) loceol,
utf8_target))
{
sayNO;
case POSIXL: /* \w or [:punct:] etc. under /l */
_CHECK_AND_WARN_PROBLEMATIC_LOCALE;
- if (NEXTCHR_IS_EOS)
+ if (NEXTCHR_IS_EOS || locinput >= loceol)
sayNO;
/* Use isFOO_lc() for characters within Latin1. (Note that
case NPOSIXA: /* \W or [:^punct:] etc. under /a */
- if (NEXTCHR_IS_EOS) {
+ if (NEXTCHR_IS_EOS || locinput >= loceol) {
sayNO;
}
* UTF-8, and also from NPOSIXA even in UTF-8 when the current
* character is a single byte */
- if (NEXTCHR_IS_EOS) {
+ if (NEXTCHR_IS_EOS || locinput >= loceol) {
sayNO;
}
case POSIXU: /* \w or [:punct:] etc. under /u */
utf8_posix:
- if (NEXTCHR_IS_EOS) {
+ if (NEXTCHR_IS_EOS || locinput >= loceol) {
sayNO;
}
}
break;
}
- locinput += UTF8SKIP(locinput);
+ locinput += UTF8_SAFE_SKIP(locinput, reginfo->strend);
}
break;
case CLUMP: /* Match \X: logical Unicode character. This is defined as
a Unicode extended Grapheme Cluster */
- if (NEXTCHR_IS_EOS)
+ if (NEXTCHR_IS_EOS || locinput >= loceol)
sayNO;
if (! utf8_target) {
locinput++; /* Match the . or CR */
if (nextchr == '\r' /* And if it was CR, and the next is LF,
match the LF */
- && locinput < reginfo->strend
+ && locinput < loceol
&& UCHARAT(locinput) == '\n')
{
locinput++;
* current character. (There is always a break at the
* end-of-input) */
locinput += UTF8SKIP(locinput);
- while (locinput < reginfo->strend) {
+ while (locinput < loceol) {
GCB_enum cur_gcb = getGCB_VAL_UTF8((U8*) locinput,
(U8*) reginfo->strend);
if (isGCB(prev_gcb, cur_gcb,
}
break;
- case NREFFL: /* /\g{name}/il */
+ case REFFLN: /* /\g{name}/il */
{ /* The capture buffer cases. The ones beginning with N for the
named buffers just convert to the equivalent numbered and
pretend they were called as the corresponding numbered buffer
utf8_fold_flags = FOLDEQ_LOCALE;
goto do_nref;
- case NREFFA: /* /\g{name}/iaa */
+ case REFFAN: /* /\g{name}/iaa */
folder = foldEQ_latin1;
fold_array = PL_fold_latin1;
type = REFFA;
utf8_fold_flags = FOLDEQ_UTF8_NOMIX_ASCII;
goto do_nref;
- case NREFFU: /* /\g{name}/iu */
+ case REFFUN: /* /\g{name}/iu */
folder = foldEQ_latin1;
fold_array = PL_fold_latin1;
type = REFFU;
utf8_fold_flags = 0;
goto do_nref;
- case NREFF: /* /\g{name}/i */
+ case REFFN: /* /\g{name}/i */
folder = foldEQ;
fold_array = PL_fold;
type = REFF;
utf8_fold_flags = 0;
goto do_nref;
- case NREF: /* /\g{name}/ */
+ case REFN: /* /\g{name}/ */
type = REF;
folder = NULL;
fold_array = NULL;
if (type != REF /* REF can do byte comparison */
&& (utf8_target || type == REFFU || type == REFFL))
{
- char * limit = reginfo->strend;
+ char * limit = loceol;
/* This call case insensitively compares the entire buffer
* at s, with the current input starting at locinput, but
- * not going off the end given by reginfo->strend, and
+ * not going off the end given by loceol, and
* returns in <limit> upon success, how much of the
* current input was matched */
if (! foldEQ_utf8_flags(s, NULL, endref - ln, utf8_target,
}
/* Not utf8: Inline the first character, for speed. */
- if (!NEXTCHR_IS_EOS &&
- UCHARAT(s) != nextchr &&
- (type == REF ||
- UCHARAT(s) != fold_array[nextchr]))
+ if ( ! NEXTCHR_IS_EOS
+ && locinput < loceol
+ && UCHARAT(s) != nextchr
+ && ( type == REF
+ || UCHARAT(s) != fold_array[nextchr]))
+ {
sayNO;
+ }
ln = endref - ln;
- if (locinput + ln > reginfo->strend)
+ if (locinput + ln > loceol)
sayNO;
if (ln > 1 && (type == REF
? memNE(s, locinput, ln)
PL_curpm = PL_reg_curpm;
if (logical != 2) {
- PUSH_STATE_GOTO(EVAL_B, next, locinput);
+ PUSH_STATE_GOTO(EVAL_B, next, locinput, loceol,
+ script_run_begin);
/* NOTREACHED */
}
}
ST.prev_eval = cur_eval;
cur_eval = st;
/* now continue from first node in postoned RE */
- PUSH_YES_STATE_GOTO(EVAL_postponed_AB, startpoint, locinput);
+ PUSH_YES_STATE_GOTO(EVAL_postponed_AB, startpoint, locinput,
+ loceol, script_run_begin);
NOT_REACHED; /* NOTREACHED */
}
sw = cBOOL(rex->lastparen >= n && rex->offs[n].end != -1);
break;
- case NGROUPP: /* (?(<name>)) */
+ case GROUPPN: /* (?(<name>)) */
/* reg_check_named_buff_matched returns 0 for no match */
sw = cBOOL(0 < reg_check_named_buff_matched(rex,scan));
break;
ST.count = -1; /* this will be updated by WHILEM */
ST.lastloc = NULL; /* this will be updated by WHILEM */
- PUSH_YES_STATE_GOTO(CURLYX_end, PREVOPER(next), locinput);
+ PUSH_YES_STATE_GOTO(CURLYX_end, PREVOPER(next), locinput, loceol,
+ script_run_begin);
NOT_REACHED; /* NOTREACHED */
}
cur_curlyx->u.curlyx.lastloc = locinput;
REGCP_SET(ST.lastcp);
- PUSH_STATE_GOTO(WHILEM_A_pre, A, locinput);
+ PUSH_STATE_GOTO(WHILEM_A_pre, A, locinput, loceol,
+ script_run_begin);
NOT_REACHED; /* NOTREACHED */
}
ST.save_curlyx = cur_curlyx;
cur_curlyx = cur_curlyx->u.curlyx.prev_curlyx;
PUSH_YES_STATE_GOTO(WHILEM_B_min, ST.save_curlyx->u.curlyx.B,
- locinput);
+ locinput, loceol, script_run_begin);
NOT_REACHED; /* NOTREACHED */
}
maxopenparen);
cur_curlyx->u.curlyx.lastloc = locinput;
REGCP_SET(ST.lastcp);
- PUSH_STATE_GOTO(WHILEM_A_max, A, locinput);
+ PUSH_STATE_GOTO(WHILEM_A_max, A, locinput, loceol,
+ script_run_begin);
NOT_REACHED; /* NOTREACHED */
}
goto do_whilem_B_max;
ST.save_curlyx = cur_curlyx;
cur_curlyx = cur_curlyx->u.curlyx.prev_curlyx;
PUSH_YES_STATE_GOTO(WHILEM_B_max, ST.save_curlyx->u.curlyx.B,
- locinput);
+ locinput, loceol, script_run_begin);
NOT_REACHED; /* NOTREACHED */
case WHILEM_B_min_fail: /* just failed to match B in a minimal match */
REGCP_SET(ST.lastcp);
PUSH_STATE_GOTO(WHILEM_A_min,
/*A*/ NEXTOPER(ST.save_curlyx->u.curlyx.me) + EXTRA_STEP_2ARGS,
- locinput);
+ locinput, loceol, script_run_begin);
NOT_REACHED; /* NOTREACHED */
#undef ST
/* Now go into the branch */
if (has_cutgroup) {
- PUSH_YES_STATE_GOTO(BRANCH_next, scan, locinput);
+ PUSH_YES_STATE_GOTO(BRANCH_next, scan, locinput, loceol,
+ script_run_begin);
} else {
- PUSH_STATE_GOTO(BRANCH_next, scan, locinput);
+ PUSH_STATE_GOTO(BRANCH_next, scan, locinput, loceol,
+ script_run_begin);
}
NOT_REACHED; /* NOTREACHED */
sv_yes_mark = st->u.mark.mark_name = scan->flags
? MUTABLE_SV(rexi->data->data[ ARG( scan ) ])
: NULL;
- PUSH_STATE_GOTO(CUTGROUP_next, next, locinput);
+ PUSH_STATE_GOTO(CUTGROUP_next, next, locinput, loceol,
+ script_run_begin);
NOT_REACHED; /* NOTREACHED */
case CUTGROUP_next_fail:
goto curlym_do_B;
curlym_do_A: /* execute the A in /A{m,n}B/ */
- PUSH_YES_STATE_GOTO(CURLYM_A, ST.A, locinput); /* match A */
+ PUSH_YES_STATE_GOTO(CURLYM_A, ST.A, locinput, loceol, /* match A */
+ script_run_begin);
NOT_REACHED; /* NOTREACHED */
case CURLYM_A: /* we've just matched an A */
);
if (! NEXTCHR_IS_EOS && ST.c1 != CHRTEST_VOID) {
if (! UTF8_IS_INVARIANT(nextchr) && utf8_target) {
- if (memNE(locinput, ST.c1_utf8, UTF8SKIP(locinput))
- && memNE(locinput, ST.c2_utf8, UTF8SKIP(locinput)))
+
+ /* (We can use memEQ and memNE in this file without
+ * having to worry about one being shorter than the
+ * other, since the first byte of each gives the
+ * length of the character) */
+ if ( memNE(locinput, ST.c1_utf8, UTF8_SAFE_SKIP(locinput,
+ reginfo->strend))
+ && memNE(locinput, ST.c2_utf8, UTF8_SAFE_SKIP(locinput,
+ reginfo->strend)))
{
/* simulate B failing */
DEBUG_OPTIMISE_r(
}
}
- PUSH_STATE_GOTO(CURLYM_B, ST.B, locinput); /* match B */
+ PUSH_STATE_GOTO(CURLYM_B, ST.B, locinput, loceol, /* match B */
+ script_run_begin);
NOT_REACHED; /* NOTREACHED */
case CURLYM_B_fail: /* just failed to match a B */
if (EVAL_CLOSE_PAREN_IS_TRUE(cur_eval,(U32)ST.paren))
{
char *li = locinput;
- if (!regrepeat(rex, &li, scan, reginfo, 1))
+ if (!regrepeat(rex, &li, scan, loceol, reginfo, 1))
sayNO;
SET_locinput(li);
goto fake_end;
char *li = locinput;
minmod = 0;
if (ST.min &&
- regrepeat(rex, &li, ST.A, reginfo, ST.min)
+ regrepeat(rex, &li, ST.A, loceol, reginfo, ST.min)
< ST.min)
sayNO;
SET_locinput(li);
/* set ST.maxpos to the furthest point along the
* string that could possibly match */
if (ST.max == REG_INFTY) {
- ST.maxpos = reginfo->strend - 1;
+ ST.maxpos = loceol - 1;
if (utf8_target)
while (UTF8_IS_CONTINUATION(*(U8*)ST.maxpos))
ST.maxpos--;
else if (utf8_target) {
int m = ST.max - ST.min;
for (ST.maxpos = locinput;
- m >0 && ST.maxpos < reginfo->strend; m--)
+ m >0 && ST.maxpos < loceol; m--)
ST.maxpos += UTF8SKIP(ST.maxpos);
}
else {
ST.maxpos = locinput + ST.max - ST.min;
- if (ST.maxpos >= reginfo->strend)
- ST.maxpos = reginfo->strend - 1;
+ if (ST.maxpos >= loceol)
+ ST.maxpos = loceol - 1;
}
goto curly_try_B_min_known;
/* avoid taking address of locinput, so it can remain
* a register var */
char *li = locinput;
- ST.count = regrepeat(rex, &li, ST.A, reginfo, ST.max);
+ ST.count = regrepeat(rex, &li, ST.A, loceol, reginfo, ST.max);
if (ST.count < ST.min)
sayNO;
SET_locinput(li);
if (ST.c1 == CHRTEST_VOID) {
/* failed -- move forward one */
char *li = locinput;
- if (!regrepeat(rex, &li, ST.A, reginfo, 1)) {
+ if (!regrepeat(rex, &li, ST.A, loceol, reginfo, 1)) {
sayNO;
}
locinput = li;
n = (ST.oldloc == locinput) ? 0 : 1;
if (ST.c1 == ST.c2) {
/* set n to utf8_distance(oldloc, locinput) */
- while (locinput <= ST.maxpos
- && memNE(locinput, ST.c1_utf8, UTF8SKIP(locinput)))
+ while ( locinput <= ST.maxpos
+ && locinput < loceol
+ && memNE(locinput, ST.c1_utf8,
+ UTF8_SAFE_SKIP(locinput, reginfo->strend)))
{
- locinput += UTF8SKIP(locinput);
+ locinput += UTF8_SAFE_SKIP(locinput,
+ reginfo->strend);
n++;
}
}
else {
/* set n to utf8_distance(oldloc, locinput) */
- while (locinput <= ST.maxpos
- && memNE(locinput, ST.c1_utf8, UTF8SKIP(locinput))
- && memNE(locinput, ST.c2_utf8, UTF8SKIP(locinput)))
+ while ( locinput <= ST.maxpos
+ && locinput < loceol
+ && memNE(locinput, ST.c1_utf8,
+ UTF8_SAFE_SKIP(locinput, reginfo->strend))
+ && memNE(locinput, ST.c2_utf8,
+ UTF8_SAFE_SKIP(locinput, reginfo->strend)))
{
- locinput += UTF8SKIP(locinput);
+ locinput += UTF8_SAFE_SKIP(locinput, reginfo->strend);
n++;
}
}
* locinput matches */
char *li = ST.oldloc;
ST.count += n;
- if (regrepeat(rex, &li, ST.A, reginfo, n) < n)
+ if (regrepeat(rex, &li, ST.A, loceol, reginfo, n) < n)
sayNO;
assert(n == REG_INFTY || locinput == li);
}
curly_try_B_min:
CURLY_SETPAREN(ST.paren, ST.count);
- PUSH_STATE_GOTO(CURLY_B_min, ST.B, locinput);
+ PUSH_STATE_GOTO(CURLY_B_min, ST.B, locinput, loceol,
+ script_run_begin);
NOT_REACHED; /* NOTREACHED */
curly_try_B_max:
/* a successful greedy match: now try to match B */
{
- bool could_match = locinput < reginfo->strend;
+ bool could_match = locinput < loceol;
/* If it could work, try it. */
if (ST.c1 != CHRTEST_VOID && could_match) {
if (! UTF8_IS_INVARIANT(UCHARAT(locinput)) && utf8_target)
{
- could_match = memEQ(locinput,
- ST.c1_utf8,
- UTF8SKIP(locinput))
- || memEQ(locinput,
- ST.c2_utf8,
- UTF8SKIP(locinput));
+ could_match = memEQ(locinput, ST.c1_utf8,
+ UTF8_SAFE_SKIP(locinput,
+ reginfo->strend))
+ || memEQ(locinput, ST.c2_utf8,
+ UTF8_SAFE_SKIP(locinput,
+ reginfo->strend));
}
else {
- could_match = UCHARAT(locinput) == ST.c1
- || UCHARAT(locinput) == ST.c2;
+ could_match = UCHARAT(locinput) == ST.c1
+ || UCHARAT(locinput) == ST.c2;
}
}
if (ST.c1 == CHRTEST_VOID || could_match) {
CURLY_SETPAREN(ST.paren, ST.count);
- PUSH_STATE_GOTO(CURLY_B_max, ST.B, locinput);
+ PUSH_STATE_GOTO(CURLY_B_max, ST.B, locinput, loceol,
+ script_run_begin);
NOT_REACHED; /* NOTREACHED */
}
}
SET_RECURSE_LOCINPUT("FAKE-END[after]", cur_eval->locinput);
- PUSH_YES_STATE_GOTO(EVAL_postponed_AB, st->u.eval.prev_eval->u.eval.B,
- locinput); /* match B */
+ PUSH_YES_STATE_GOTO(EVAL_postponed_AB, /* match B */
+ st->u.eval.prev_eval->u.eval.B,
+ locinput, loceol, script_run_begin);
}
if (locinput < reginfo->till) {
#undef ST
#define ST st->u.ifmatch
- {
- char *newstart;
-
case SUSPEND: /* (?>A) */
ST.wanted = 1;
- newstart = locinput;
+ ST.start = locinput;
+ ST.end = loceol;
+ ST.count = 1;
goto do_ifmatch;
- case UNLESSM: /* -ve lookaround: (?!A), or with flags, (?<!A) */
+ case UNLESSM: /* -ve lookaround: (?!A), or with 'flags', (?<!A) */
ST.wanted = 0;
goto ifmatch_trivial_fail_test;
- case IFMATCH: /* +ve lookaround: (?=A), or with flags, (?<=A) */
+ case IFMATCH: /* +ve lookaround: (?=A), or with 'flags', (?<=A) */
ST.wanted = 1;
ifmatch_trivial_fail_test:
- if (scan->flags) {
- char * const s = HOPBACKc(locinput, scan->flags);
- if (!s) {
- /* trivial fail */
- if (logical) {
- logical = 0;
- sw = 1 - cBOOL(ST.wanted);
- }
- else if (ST.wanted)
- sayNO;
- next = scan + ARG(scan);
- if (next == scan)
- next = NULL;
- break;
- }
- newstart = s;
+ ST.count = scan->next_off + 1; /* next_off repurposed to be
+ lookbehind count, requires
+ non-zero flags */
+ if (! scan->flags) { /* 'flags' zero means lookahed */
+
+ /* Lookahead starts here and ends at the normal place */
+ ST.start = locinput;
+ ST.end = loceol;
+ }
+ else {
+ PERL_UINT_FAST8_T back_count = scan->flags;
+ char * s;
+
+ /* Lookbehind can look beyond the current position */
+ ST.end = loceol;
+
+ /* ... and starts at the first place in the input that is in
+ * the range of the possible start positions */
+ for (; ST.count > 0; ST.count--, back_count--) {
+ s = HOPBACKc(locinput, back_count);
+ if (s) {
+ ST.start = s;
+ goto do_ifmatch;
+ }
+ }
+
+ /* If the lookbehind doesn't start in the actual string, is a
+ * trivial match failure */
+ if (logical) {
+ logical = 0;
+ sw = 1 - cBOOL(ST.wanted);
+ }
+ else if (ST.wanted)
+ sayNO;
+
+ /* Here, we didn't want it to match, so is actually success */
+ next = scan + ARG(scan);
+ if (next == scan)
+ next = NULL;
+ break;
}
- else
- newstart = locinput;
do_ifmatch:
ST.me = scan;
logical = 0; /* XXX: reset state of logical once it has been saved into ST */
/* execute body of (?...A) */
- PUSH_YES_STATE_GOTO(IFMATCH_A, NEXTOPER(NEXTOPER(scan)), newstart);
+ PUSH_YES_STATE_GOTO(IFMATCH_A, NEXTOPER(NEXTOPER(scan)), ST.start,
+ ST.end, script_run_begin);
NOT_REACHED; /* NOTREACHED */
- }
+
+ {
+ bool matched;
case IFMATCH_A_fail: /* body of (?...A) failed */
- ST.wanted = !ST.wanted;
- /* FALLTHROUGH */
+ if (! ST.logical && ST.count > 1) {
+
+ /* It isn't a real failure until we've tried all starting
+ * positions. Move to the next starting position and retry */
+ ST.count--;
+ ST.start = HOPc(ST.start, 1);
+ scan = ST.me;
+ logical = ST.logical;
+ goto do_ifmatch;
+ }
+
+ /* Here, all starting positions have been tried. */
+ matched = FALSE;
+ goto ifmatch_done;
case IFMATCH_A: /* body of (?...A) succeeded */
- if (ST.logical) {
- sw = cBOOL(ST.wanted);
- }
- else if (!ST.wanted)
- sayNO;
+ matched = TRUE;
+ ifmatch_done:
+ sw = matched == ST.wanted;
+ if (! ST.logical && !sw) {
+ sayNO;
+ }
if (OP(ST.me) != SUSPEND) {
/* restore old position except for (?>...) */
locinput = st->locinput;
+ loceol = st->loceol;
+ script_run_begin = st->sr0;
}
scan = ST.me + ARG(ST.me);
if (scan == ST.me)
scan = NULL;
continue; /* execute B */
+ }
#undef ST
break;
case COMMIT: /* (*COMMIT) */
- reginfo->cutpoint = reginfo->strend;
+ reginfo->cutpoint = loceol;
/* FALLTHROUGH */
case PRUNE: /* (*PRUNE) */
if (scan->flags)
sv_yes_mark = sv_commit = MUTABLE_SV(rexi->data->data[ ARG( scan ) ]);
- PUSH_STATE_GOTO(COMMIT_next, next, locinput);
+ PUSH_STATE_GOTO(COMMIT_next, next, locinput, loceol,
+ script_run_begin);
NOT_REACHED; /* NOTREACHED */
case COMMIT_next_fail:
= MUTABLE_SV(rexi->data->data[ ARG( scan ) ]);
mark_state = st;
ST.mark_loc = locinput;
- PUSH_YES_STATE_GOTO(MARKPOINT_next, next, locinput);
+ PUSH_YES_STATE_GOTO(MARKPOINT_next, next, locinput, loceol,
+ script_run_begin);
NOT_REACHED; /* NOTREACHED */
case MARKPOINT_next:
/* (*SKIP) : if we fail we cut here*/
ST.mark_name = NULL;
ST.mark_loc = locinput;
- PUSH_STATE_GOTO(SKIP_next,next, locinput);
+ PUSH_STATE_GOTO(SKIP_next,next, locinput, loceol,
+ script_run_begin);
} else {
/* (*SKIP:NAME) : if there is a (*MARK:NAME) fail where it was,
otherwise do nothing. Meaning we need to scan
find ) )
{
ST.mark_name = find;
- PUSH_STATE_GOTO( SKIP_next, next, locinput);
+ PUSH_STATE_GOTO( SKIP_next, next, locinput, loceol,
+ script_run_begin);
}
cur = cur->u.mark.prev_mark;
}
#undef ST
case LNBREAK: /* \R */
- if ((n=is_LNBREAK_safe(locinput, reginfo->strend, utf8_target))) {
+ if ((n=is_LNBREAK_safe(locinput, loceol, utf8_target))) {
locinput += n;
} else
sayNO;
locinput += PL_utf8skip[nextchr];
/* locinput is allowed to go 1 char off the end (signifying
* EOS), but not 2+ */
- if (locinput > reginfo->strend)
+ if (locinput > loceol)
sayNO;
}
else
);
depth++;
st->locinput = locinput;
+ st->loceol = loceol;
+ st->sr0 = script_run_begin;
newst = st+1;
if (newst > SLAB_LAST(PL_regmatch_slab))
newst = S_push_slab(aTHX);
PL_regmatch_state = newst;
locinput = pushinput;
+ loceol = pusheol;
+ script_run_begin = pushsr0;
st = newst;
continue;
/* NOTREACHED */
yes_state = st->u.yes.prev_yes_state;
PL_regmatch_state = st;
- if (no_final)
+ if (no_final) {
locinput= st->locinput;
+ loceol= st->loceol;
+ script_run_begin = st->sr0;
+ }
state_num = st->resume_state + no_final;
goto reenter_switch;
}
}
PL_regmatch_state = st;
locinput= st->locinput;
+ loceol= st->loceol;
+ script_run_begin = st->sr0;
DEBUG_STATE_pp("pop");
depth--;
* What 'simple' means is a node which can be the operand of a quantifier like
* '+', or {1,3}
*
- * startposp - pointer a pointer to the start position. This is updated
+ * startposp - pointer to a pointer to the start position. This is updated
* to point to the byte following the highest successful
* match.
* p - the regnode to be repeatedly matched against.
- * reginfo - struct holding match state, such as strend
+ * loceol - pointer to the end position beyond which we aren't supposed to
+ * look.
+ * reginfo - struct holding match state, such as utf8_target
* max - maximum number of things to match.
* depth - (for debugging) backtracking depth.
*/
STATIC I32
S_regrepeat(pTHX_ regexp *prog, char **startposp, const regnode *p,
- regmatch_info *const reginfo, I32 max _pDEPTH)
+ char * loceol, regmatch_info *const reginfo, I32 max _pDEPTH)
{
dVAR;
char *scan; /* Pointer to current position in target string */
I32 c;
- char *loceol = reginfo->strend; /* local version */
+ char *this_eol = loceol; /* potentially adjusted version. */
I32 hardcount = 0; /* How many matches so far */
bool utf8_target = reginfo->is_utf8_target;
unsigned int to_complement = 0; /* Invert the result? */
PERL_ARGS_ASSERT_REGREPEAT;
+ /* This routine is structured so that we switch on the input OP. Each OP
+ * case: statement contains a loop to repeatedly apply the OP, advancing
+ * the input until it fails, or reaches the end of the input, or until it
+ * reaches the upper limit of matches. */
+
scan = *startposp;
- if (max == REG_INFTY)
+ if (max == REG_INFTY) /* This is a special marker to go to the platform's
+ max */
max = I32_MAX;
- else if (! utf8_target && loceol - scan > max)
- loceol = scan + max;
+ else if (! utf8_target && this_eol - scan > max)
+ this_eol = scan + max;
- /* Here, for the case of a non-UTF-8 target we have adjusted <loceol> down
+ /* Here, for the case of a non-UTF-8 target we have adjusted <this_eol> down
* to the maximum of how far we should go in it (leaving it set to the real
* end, if the maximum permissible would take us beyond that). This allows
- * us to make the loop exit condition that we haven't gone past <loceol> to
+ * us to make the loop exit condition that we haven't gone past <this_eol> to
* also mean that we haven't exceeded the max permissible count, saving a
- * test each time through the loop. But it assumes that the OP matches a
+ * test each time through the loops. But it assumes that the OP matches a
* single byte, which is true for most of the OPs below when applied to a
* non-UTF-8 target. Those relatively few OPs that don't have this
* characteristic will have to compensate.
* There is no adjustment for UTF-8 targets, as the number of bytes per
* character varies. OPs will have to test both that the count is less
* than the max permissible (using <hardcount> to keep track), and that we
- * are still within the bounds of the string (using <loceol>. A few OPs
+ * are still within the bounds of the string (using <this_eol>. A few OPs
* match a single byte no matter what the encoding. They can omit the max
* test if, for the UTF-8 case, they do the adjustment that was skipped
* above.
*
* Thus, the code above sets things up for the common case; and exceptional
* cases need extra work; the common case is to make sure <scan> doesn't
- * go past <loceol>, and for UTF-8 to also use <hardcount> to make sure the
+ * go past <this_eol>, and for UTF-8 to also use <hardcount> to make sure the
* count doesn't exceed the maximum permissible */
switch (OP(p)) {
case REG_ANY:
if (utf8_target) {
- while (scan < loceol && hardcount < max && *scan != '\n') {
+ while (scan < this_eol && hardcount < max && *scan != '\n') {
scan += UTF8SKIP(scan);
hardcount++;
}
} else {
- scan = (char *) memchr(scan, '\n', loceol - scan);
+ scan = (char *) memchr(scan, '\n', this_eol - scan);
if (! scan) {
- scan = loceol;
+ scan = this_eol;
}
}
break;
case SANY:
if (utf8_target) {
- while (scan < loceol && hardcount < max) {
+ while (scan < this_eol && hardcount < max) {
scan += UTF8SKIP(scan);
hardcount++;
}
}
else
- scan = loceol;
+ scan = this_eol;
break;
case EXACTL:
_CHECK_AND_WARN_PROBLEMATIC_LOCALE;
* can use UTF8_IS_INVARIANT() even if the pattern isn't UTF-8, as it's
* true iff it doesn't matter if the argument is in UTF-8 or not */
if (UTF8_IS_INVARIANT(c) || (! utf8_target && ! reginfo->is_utf8_pat)) {
- if (utf8_target && loceol - scan > max) {
- /* We didn't adjust <loceol> because is UTF-8, but ok to do so,
+ if (utf8_target && this_eol - scan > max) {
+ /* We didn't adjust <this_eol> because is UTF-8, but ok to do so,
* since here, to match at all, 1 char == 1 byte */
- loceol = scan + max;
+ this_eol = scan + max;
}
- scan = (char *) find_span_end((U8 *) scan, (U8 *) loceol, (U8) c);
+ scan = (char *) find_span_end((U8 *) scan, (U8 *) this_eol, (U8) c);
}
else if (reginfo->is_utf8_pat) {
if (utf8_target) {
/* When both target and pattern are UTF-8, we have to do
* string EQ */
while (hardcount < max
- && scan < loceol
+ && scan < this_eol
&& (scan_char_len = UTF8SKIP(scan)) <= STR_LEN(p)
&& memEQ(scan, STRING(p), scan_char_len))
{
/* Target isn't utf8; convert the character in the UTF-8
* pattern to non-UTF8, and do a simple find */
c = EIGHT_BIT_UTF8_TO_NATIVE(c, *(STRING(p) + 1));
- scan = (char *) find_span_end((U8 *) scan, (U8 *) loceol, (U8) c);
+ scan = (char *) find_span_end((U8 *) scan, (U8 *) this_eol, (U8) c);
} /* else pattern char is above Latin1, can't possibly match the
non-UTF-8 target */
}
U8 low = UTF8_TWO_BYTE_LO(c);
while (hardcount < max
- && scan + 1 < loceol
+ && scan + 1 < this_eol
&& UCHARAT(scan) == high
&& UCHARAT(scan + 1) == low)
{
{
if (c1 == CHRTEST_VOID) {
/* Use full Unicode fold matching */
- char *tmpeol = reginfo->strend;
+ char *tmpeol = loceol;
STRLEN pat_len = reginfo->is_utf8_pat ? UTF8SKIP(STRING(p)) : 1;
while (hardcount < max
&& foldEQ_utf8_flags(scan, &tmpeol, 0, utf8_target,
reginfo->is_utf8_pat, utf8_flags))
{
scan = tmpeol;
- tmpeol = reginfo->strend;
+ tmpeol = loceol;
hardcount++;
}
}
else if (utf8_target) {
if (c1 == c2) {
- while (scan < loceol
+ while (scan < this_eol
&& hardcount < max
- && memEQ(scan, c1_utf8, UTF8SKIP(scan)))
+ && memEQ(scan, c1_utf8, UTF8_SAFE_SKIP(scan,
+ loceol)))
{
- scan += UTF8SKIP(scan);
+ scan += UTF8SKIP(c1_utf8);
hardcount++;
}
}
else {
- while (scan < loceol
+ while (scan < this_eol
&& hardcount < max
- && (memEQ(scan, c1_utf8, UTF8SKIP(scan))
- || memEQ(scan, c2_utf8, UTF8SKIP(scan))))
+ && ( memEQ(scan, c1_utf8, UTF8_SAFE_SKIP(scan,
+ loceol))
+ || memEQ(scan, c2_utf8, UTF8_SAFE_SKIP(scan,
+ loceol))))
{
- scan += UTF8SKIP(scan);
+ scan += UTF8_SAFE_SKIP(scan, loceol);
hardcount++;
}
}
}
else if (c1 == c2) {
- scan = (char *) find_span_end((U8 *) scan, (U8 *) loceol, (U8) c1);
+ scan = (char *) find_span_end((U8 *) scan, (U8 *) this_eol, (U8) c1);
}
else {
/* See comments in regmatch() CURLY_B_min_known_fail. We avoid
U8 c1_c2_mask = ~ c1_c2_bits_differing;
scan = (char *) find_span_end_mask((U8 *) scan,
- (U8 *) loceol,
+ (U8 *) this_eol,
c1 & c1_c2_mask,
c1_c2_mask);
}
else {
- while ( scan < loceol
+ while ( scan < this_eol
&& (UCHARAT(scan) == c1 || UCHARAT(scan) == c2))
{
scan++;
case ANYOF:
if (utf8_target) {
while (hardcount < max
- && scan < loceol
- && reginclass(prog, p, (U8*)scan, (U8*) loceol, utf8_target))
+ && scan < this_eol
+ && reginclass(prog, p, (U8*)scan, (U8*) this_eol, utf8_target))
{
scan += UTF8SKIP(scan);
hardcount++;
}
}
else if (ANYOF_FLAGS(p) & ~ ANYOF_MATCHES_ALL_ABOVE_BITMAP) {
- while (scan < loceol
+ while (scan < this_eol
&& reginclass(prog, p, (U8*)scan, (U8*)scan+1, 0))
scan++;
}
else {
- while (scan < loceol && ANYOF_BITMAP_TEST(p, *((U8*)scan)))
+ while (scan < this_eol && ANYOF_BITMAP_TEST(p, *((U8*)scan)))
scan++;
}
break;
case ANYOFM:
- if (utf8_target && loceol - scan > max) {
+ if (utf8_target && this_eol - scan > max) {
- /* We didn't adjust <loceol> at the beginning of this routine
+ /* We didn't adjust <this_eol> at the beginning of this routine
* because is UTF-8, but it is actually ok to do so, since here, to
* match, 1 char == 1 byte. */
- loceol = scan + max;
+ this_eol = scan + max;
}
- scan = (char *) find_span_end_mask((U8 *) scan, (U8 *) loceol, (U8) ARG(p), FLAGS(p));
+ scan = (char *) find_span_end_mask((U8 *) scan, (U8 *) this_eol, (U8) ARG(p), FLAGS(p));
break;
case NANYOFM:
if (utf8_target) {
while ( hardcount < max
- && scan < loceol
+ && scan < this_eol
&& (*scan & FLAGS(p)) != ARG(p))
{
scan += UTF8SKIP(scan);
}
}
else {
- scan = (char *) find_next_masked((U8 *) scan, (U8 *) loceol, (U8) ARG(p), FLAGS(p));
+ scan = (char *) find_next_masked((U8 *) scan, (U8 *) this_eol, (U8) ARG(p), FLAGS(p));
}
break;
case ANYOFH:
- if (utf8_target) while ( hardcount < max
- && scan < loceol
- && reginclass(prog, p, (U8*)scan, (U8*) loceol,
- TRUE))
- {
- scan += UTF8SKIP(scan);
- hardcount++;
+ if (utf8_target) { /* ANYOFH only can match UTF-8 targets */
+ while ( hardcount < max
+ && scan < this_eol
+ && NATIVE_UTF8_TO_I8((U8) *scan) >= ANYOF_FLAGS(p)
+ && reginclass(prog, p, (U8*)scan, (U8*) this_eol, TRUE))
+ {
+ scan += UTF8SKIP(scan);
+ hardcount++;
+ }
+ }
+ break;
+
+ case ANYOFHb:
+ if (utf8_target) { /* ANYOFHb only can match UTF-8 targets */
+
+ /* we know the first byte must be the FLAGS field */
+ while ( hardcount < max
+ && scan < this_eol
+ && (U8) *scan == ANYOF_FLAGS(p)
+ && reginclass(prog, p, (U8*)scan, (U8*) this_eol,
+ TRUE))
+ {
+ scan += UTF8SKIP(scan);
+ hardcount++;
+ }
+ }
+ break;
+
+ case ANYOFHr:
+ if (utf8_target) { /* ANYOFH only can match UTF-8 targets */
+ while ( hardcount < max
+ && scan < this_eol
+ && inRANGE((U8) NATIVE_UTF8_TO_I8(*scan),
+ LOWEST_ANYOF_HRx_BYTE(ANYOF_FLAGS(p)),
+ HIGHEST_ANYOF_HRx_BYTE(ANYOF_FLAGS(p)))
+ && NATIVE_UTF8_TO_I8((U8) *scan) >= ANYOF_FLAGS(p)
+ && reginclass(prog, p, (U8*)scan, (U8*) this_eol, TRUE))
+ {
+ scan += UTF8SKIP(scan);
+ hardcount++;
+ }
}
break;
case POSIXL:
_CHECK_AND_WARN_PROBLEMATIC_LOCALE;
if (! utf8_target) {
- while (scan < loceol && to_complement ^ cBOOL(isFOO_lc(FLAGS(p),
+ while (scan < this_eol && to_complement ^ cBOOL(isFOO_lc(FLAGS(p),
*scan)))
{
scan++;
}
} else {
- while (hardcount < max && scan < loceol
+ while (hardcount < max && scan < this_eol
&& to_complement ^ cBOOL(isFOO_utf8_lc(FLAGS(p),
(U8 *) scan,
- (U8 *) loceol)))
+ (U8 *) this_eol)))
{
scan += UTF8SKIP(scan);
hardcount++;
/* FALLTHROUGH */
case POSIXA:
- if (utf8_target && loceol - scan > max) {
+ if (utf8_target && this_eol - scan > max) {
- /* We didn't adjust <loceol> at the beginning of this routine
+ /* We didn't adjust <this_eol> at the beginning of this routine
* because is UTF-8, but it is actually ok to do so, since here, to
* match, 1 char == 1 byte. */
- loceol = scan + max;
+ this_eol = scan + max;
}
- while (scan < loceol && _generic_isCC_A((U8) *scan, FLAGS(p))) {
+ while (scan < this_eol && _generic_isCC_A((U8) *scan, FLAGS(p))) {
scan++;
}
break;
case NPOSIXA:
if (! utf8_target) {
- while (scan < loceol && ! _generic_isCC_A((U8) *scan, FLAGS(p))) {
+ while (scan < this_eol && ! _generic_isCC_A((U8) *scan, FLAGS(p))) {
scan++;
}
}
/* The complement of something that matches only ASCII matches all
* non-ASCII, plus everything in ASCII that isn't in the class. */
- while (hardcount < max && scan < loceol
- && ( ! isASCII_utf8_safe(scan, reginfo->strend)
+ while (hardcount < max && scan < this_eol
+ && ( ! isASCII_utf8_safe(scan, loceol)
|| ! _generic_isCC_A((U8) *scan, FLAGS(p))))
{
scan += UTF8SKIP(scan);
case POSIXU:
if (! utf8_target) {
- while (scan < loceol && to_complement
+ while (scan < this_eol && to_complement
^ cBOOL(_generic_isCC((U8) *scan, FLAGS(p))))
{
scan++;
classnum = (_char_class_number) FLAGS(p);
switch (classnum) {
default:
- while ( hardcount < max && scan < loceol
+ while ( hardcount < max && scan < this_eol
&& to_complement ^ cBOOL(_invlist_contains_cp(
PL_XPosix_ptrs[classnum],
utf8_to_uvchr_buf((U8 *) scan,
- (U8 *) loceol,
+ (U8 *) this_eol,
NULL))))
{
scan += UTF8SKIP(scan);
case _CC_ENUM_SPACE:
while (hardcount < max
- && scan < loceol
+ && scan < this_eol
&& (to_complement
- ^ cBOOL(isSPACE_utf8_safe(scan, loceol))))
+ ^ cBOOL(isSPACE_utf8_safe(scan, this_eol))))
{
scan += UTF8SKIP(scan);
hardcount++;
break;
case _CC_ENUM_BLANK:
while (hardcount < max
- && scan < loceol
+ && scan < this_eol
&& (to_complement
- ^ cBOOL(isBLANK_utf8_safe(scan, loceol))))
+ ^ cBOOL(isBLANK_utf8_safe(scan, this_eol))))
{
scan += UTF8SKIP(scan);
hardcount++;
break;
case _CC_ENUM_XDIGIT:
while (hardcount < max
- && scan < loceol
+ && scan < this_eol
&& (to_complement
- ^ cBOOL(isXDIGIT_utf8_safe(scan, loceol))))
+ ^ cBOOL(isXDIGIT_utf8_safe(scan, this_eol))))
{
scan += UTF8SKIP(scan);
hardcount++;
break;
case _CC_ENUM_VERTSPACE:
while (hardcount < max
- && scan < loceol
+ && scan < this_eol
&& (to_complement
- ^ cBOOL(isVERTWS_utf8_safe(scan, loceol))))
+ ^ cBOOL(isVERTWS_utf8_safe(scan, this_eol))))
{
scan += UTF8SKIP(scan);
hardcount++;
break;
case _CC_ENUM_CNTRL:
while (hardcount < max
- && scan < loceol
+ && scan < this_eol
&& (to_complement
- ^ cBOOL(isCNTRL_utf8_safe(scan, loceol))))
+ ^ cBOOL(isCNTRL_utf8_safe(scan, this_eol))))
{
scan += UTF8SKIP(scan);
hardcount++;
case LNBREAK:
if (utf8_target) {
- while (hardcount < max && scan < loceol &&
- (c=is_LNBREAK_utf8_safe(scan, loceol))) {
+ while (hardcount < max && scan < this_eol &&
+ (c=is_LNBREAK_utf8_safe(scan, this_eol))) {
scan += c;
hardcount++;
}
} else {
/* LNBREAK can match one or two latin chars, which is ok, but we
* have to use hardcount in this situation, and throw away the
- * adjustment to <loceol> done before the switch statement */
- loceol = reginfo->strend;
+ * adjustment to <this_eol> done before the switch statement */
while (scan < loceol && (c=is_LNBREAK_latin1_safe(scan, loceol))) {
scan+=c;
hardcount++;
S_reginclass(pTHX_ regexp * const prog, const regnode * const n, const U8* const p, const U8* const p_end, const bool utf8_target)
{
dVAR;
- const char flags = ANYOF_FLAGS(n);
+ const char flags = (inRANGE(OP(n), ANYOFH, ANYOFHr))
+ ? 0
+ : ANYOF_FLAGS(n);
bool match = FALSE;
UV c = *p;
}
/* If this character is potentially in the bitmap, check it */
- if (c < NUM_ANYOF_CODE_POINTS && OP(n) != ANYOFH) {
+ if (c < NUM_ANYOF_CODE_POINTS && ! inRANGE(OP(n), ANYOFH, ANYOFHb)) {
if (ANYOF_BITMAP_TEST(n, c))
match = TRUE;
else if ((flags
regmatch_info_aux_eval *eval_state = reginfo->info_aux_eval;
eval_state->rex = rex;
+ eval_state->sv = reginfo->sv;
if (reginfo->sv) {
/* Make $_ available to executed code. */
SAVE_DEFSV;
DEFSV_set(reginfo->sv);
}
+ /* will be dec'd by S_cleanup_regmatch_info_aux */
+ SvREFCNT_inc_NN(reginfo->sv);
if (!(mg = mg_find_mglob(reginfo->sv))) {
/* prepare for quick setting of pos */
}
PL_curpm = eval_state->curpm;
+ SvREFCNT_dec(eval_state->sv);
}
PL_regmatch_state = aux->old_regmatch_state;
&& !prog->substrs->data[i].substr) {
SV* sv = newSVsv(prog->substrs->data[i].utf8_substr);
if (! sv_utf8_downgrade(sv, TRUE)) {
+ SvREFCNT_dec_NN(sv);
return FALSE;
}
if (SvVALID(prog->substrs->data[i].utf8_substr)) {
For example, if all the characters in the sequence are Greek, or Common, or
Inherited, this function will return TRUE, provided any decimal digits in it
-are the ASCII digits "0".."9". For scripts (unlike Greek) that have their own
-digits defined this will accept either digits from that set or from 0..9, but
-not a combination of the two. Some scripts, such as Arabic, have more than one
-set of digits. All digits must come from the same set for this function to
-return TRUE.
+are from the same block of digits in Common. (These are the ASCII digits
+"0".."9" and additionally a block for full width forms of these, and several
+others used in mathematical notation.) For scripts (unlike Greek) that have
+their own digits defined this will accept either digits from that set or from
+one of the Common digit sets, but not a combination of the two. Some scripts,
+such as Arabic, have more than one set of digits. All digits must come from
+the same set for this function to return TRUE.
C<*ret_script>, if C<ret_script> is not NULL, will on return of TRUE
contain the script found, using the C<SCX_enum> typedef. Its value will be
UV cp;
/* The code allows all scripts to use the ASCII digits. This is
- * because they are used in commerce even in scripts that have their
- * own set. Hence any ASCII ones found are ok, unless and until a
- * digit from another set has already been encountered. (The other
- * digit ranges in Common are not similarly blessed) */
+ * because they are in the Common script. Hence any ASCII ones found
+ * are ok, unless and until a digit from another set has already been
+ * encountered. digit ranges in Common are not similarly blessed) */
if (UNLIKELY(isDIGIT(*s))) {
if (UNLIKELY(script_of_run == SCX_Unknown)) {
retval = FALSE;
/* If the run so far is Common, and the new character isn't, change the
* run's script to that of this character */
if (script_of_run == SCX_Common && script_of_char != SCX_Common) {
-
- /* But Common contains several sets of digits. Only the '0' set
- * can be part of another script. */
- if (zero_of_run && zero_of_run != '0') {
- retval = FALSE;
- break;
- }
-
script_of_run = script_of_char;
}
- /* Now we can see if the script of the character is the same as that of
- * the run */
+ /* Now we can see if the script of the new character is the same as
+ * that of the run */
if (LIKELY(script_of_char == script_of_run)) {
/* By far the most common case */
goto scripts_match;
break;
}
}
- else if (script_of_char == SCX_Common && script_of_run != SCX_Common) {
-
- /* Here, the script run isn't Common, but the current digit is in
- * Common, and isn't '0'-'9' (those were handled earlier). Only
- * '0'-'9' are acceptable in non-Common scripts. */
- retval = FALSE;
- break;
- }
else { /* Otherwise we now have a zero for this run */
zero_of_run = zero_of_char;
}