X-Git-Url: https://perl5.git.perl.org/perl5.git/blobdiff_plain/28e464fba839d02f376952199261fc8b7d58a0d8..9f4a55d4a28d81a94e54fa1913ec5c7affbce6fe:/regexec.c?ds=sidebyside diff --git a/regexec.c b/regexec.c index 83d216b..40f66a8 100644 --- a/regexec.c +++ b/regexec.c @@ -117,12 +117,130 @@ #define HOP3(pos,off,lim) (PL_reg_match_utf8 ? reghop3((U8*)(pos), off, (U8*)(lim)) : (U8*)(pos + off)) #define HOP3c(pos,off,lim) ((char*)HOP3(pos,off,lim)) +/* these are unrolled below in the CCC_TRY_XXX defined */ #define LOAD_UTF8_CHARCLASS(class,str) STMT_START { \ if (!CAT2(PL_utf8_,class)) { bool ok; ENTER; save_re_context(); ok=CAT2(is_utf8_,class)((const U8*)str); assert(ok); LEAVE; } } STMT_END + +/* Doesn't do an assert to verify that is correct */ +#define LOAD_UTF8_CHARCLASS_NO_CHECK(class) STMT_START { \ + if (!CAT2(PL_utf8_,class)) { bool ok; ENTER; save_re_context(); ok=CAT2(is_utf8_,class)((const U8*)" "); LEAVE; } } STMT_END + #define LOAD_UTF8_CHARCLASS_ALNUM() LOAD_UTF8_CHARCLASS(alnum,"a") #define LOAD_UTF8_CHARCLASS_DIGIT() LOAD_UTF8_CHARCLASS(digit,"0") #define LOAD_UTF8_CHARCLASS_SPACE() LOAD_UTF8_CHARCLASS(space," ") -#define LOAD_UTF8_CHARCLASS_MARK() LOAD_UTF8_CHARCLASS(mark, "\xcd\x86") + +#define LOAD_UTF8_CHARCLASS_GCB() /* Grapheme cluster boundaries */ \ + LOAD_UTF8_CHARCLASS(X_begin, " "); \ + LOAD_UTF8_CHARCLASS(X_non_hangul, "A"); \ + /* These are utf8 constants, and not utf-ebcdic constants, so the \ + * assert should likely and hopefully fail on an EBCDIC machine */ \ + LOAD_UTF8_CHARCLASS(X_extend, "\xcc\x80"); /* U+0300 */ \ + \ + /* No asserts are done for these, in case called on an early \ + * Unicode version in which they map to nothing */ \ + LOAD_UTF8_CHARCLASS_NO_CHECK(X_prepend);/* U+0E40 "\xe0\xb9\x80" */ \ + LOAD_UTF8_CHARCLASS_NO_CHECK(X_L); /* U+1100 "\xe1\x84\x80" */ \ + LOAD_UTF8_CHARCLASS_NO_CHECK(X_LV); /* U+AC00 "\xea\xb0\x80" */ \ + LOAD_UTF8_CHARCLASS_NO_CHECK(X_LVT); /* U+AC01 "\xea\xb0\x81" */ \ + LOAD_UTF8_CHARCLASS_NO_CHECK(X_LV_LVT_V);/* U+AC01 "\xea\xb0\x81" */\ + LOAD_UTF8_CHARCLASS_NO_CHECK(X_T); /* U+11A8 "\xe1\x86\xa8" */ \ + LOAD_UTF8_CHARCLASS_NO_CHECK(X_V) /* U+1160 "\xe1\x85\xa0" */ + +/* + We dont use PERL_LEGACY_UNICODE_CHARCLASS_MAPPINGS as the direct test + so that it is possible to override the option here without having to + rebuild the entire core. as we are required to do if we change regcomp.h + which is where PERL_LEGACY_UNICODE_CHARCLASS_MAPPINGS is defined. +*/ +#if PERL_LEGACY_UNICODE_CHARCLASS_MAPPINGS +#define BROKEN_UNICODE_CHARCLASS_MAPPINGS +#endif + +#ifdef BROKEN_UNICODE_CHARCLASS_MAPPINGS +#define LOAD_UTF8_CHARCLASS_PERL_WORD() LOAD_UTF8_CHARCLASS_ALNUM() +#define LOAD_UTF8_CHARCLASS_PERL_SPACE() LOAD_UTF8_CHARCLASS_SPACE() +#define LOAD_UTF8_CHARCLASS_POSIX_DIGIT() LOAD_UTF8_CHARCLASS_DIGIT() +#define RE_utf8_perl_word PL_utf8_alnum +#define RE_utf8_perl_space PL_utf8_space +#define RE_utf8_posix_digit PL_utf8_digit +#define perl_word alnum +#define perl_space space +#define posix_digit digit +#else +#define LOAD_UTF8_CHARCLASS_PERL_WORD() LOAD_UTF8_CHARCLASS(perl_word,"a") +#define LOAD_UTF8_CHARCLASS_PERL_SPACE() LOAD_UTF8_CHARCLASS(perl_space," ") +#define LOAD_UTF8_CHARCLASS_POSIX_DIGIT() LOAD_UTF8_CHARCLASS(posix_digit,"0") +#define RE_utf8_perl_word PL_utf8_perl_word +#define RE_utf8_perl_space PL_utf8_perl_space +#define RE_utf8_posix_digit PL_utf8_posix_digit +#endif + + +#define CCC_TRY_AFF(NAME,NAMEL,CLASS,STR,LCFUNC_utf8,FUNC,LCFUNC) \ + case NAMEL: \ + PL_reg_flags |= RF_tainted; \ + /* FALL THROUGH */ \ + case NAME: \ + if (!nextchr) \ + sayNO; \ + if (do_utf8 && UTF8_IS_CONTINUED(nextchr)) { \ + if (!CAT2(PL_utf8_,CLASS)) { \ + bool ok; \ + ENTER; \ + save_re_context(); \ + ok=CAT2(is_utf8_,CLASS)((const U8*)STR); \ + assert(ok); \ + LEAVE; \ + } \ + if (!(OP(scan) == NAME \ + ? cBOOL(swash_fetch(CAT2(PL_utf8_,CLASS), (U8*)locinput, do_utf8)) \ + : LCFUNC_utf8((U8*)locinput))) \ + { \ + sayNO; \ + } \ + locinput += PL_utf8skip[nextchr]; \ + nextchr = UCHARAT(locinput); \ + break; \ + } \ + if (!(OP(scan) == NAME ? FUNC(nextchr) : LCFUNC(nextchr))) \ + sayNO; \ + nextchr = UCHARAT(++locinput); \ + break + +#define CCC_TRY_NEG(NAME,NAMEL,CLASS,STR,LCFUNC_utf8,FUNC,LCFUNC) \ + case NAMEL: \ + PL_reg_flags |= RF_tainted; \ + /* FALL THROUGH */ \ + case NAME : \ + if (!nextchr && locinput >= PL_regeol) \ + sayNO; \ + if (do_utf8 && UTF8_IS_CONTINUED(nextchr)) { \ + if (!CAT2(PL_utf8_,CLASS)) { \ + bool ok; \ + ENTER; \ + save_re_context(); \ + ok=CAT2(is_utf8_,CLASS)((const U8*)STR); \ + assert(ok); \ + LEAVE; \ + } \ + if ((OP(scan) == NAME \ + ? cBOOL(swash_fetch(CAT2(PL_utf8_,CLASS), (U8*)locinput, do_utf8)) \ + : LCFUNC_utf8((U8*)locinput))) \ + { \ + sayNO; \ + } \ + locinput += PL_utf8skip[nextchr]; \ + nextchr = UCHARAT(locinput); \ + break; \ + } \ + if ((OP(scan) == NAME ? FUNC(nextchr) : LCFUNC(nextchr))) \ + sayNO; \ + nextchr = UCHARAT(++locinput); \ + break + + + + /* TODO: Combine JUMPABLE and HAS_TEXT to cache OP(rn) */ @@ -176,21 +294,31 @@ static void restore_pos(pTHX_ void *arg); +#define REGCP_PAREN_ELEMS 4 +#define REGCP_OTHER_ELEMS 5 +#define REGCP_FRAME_ELEMS 1 +/* REGCP_FRAME_ELEMS are not part of the REGCP_OTHER_ELEMS and + * are needed for the regexp context stack bookkeeping. */ + STATIC CHECKPOINT S_regcppush(pTHX_ I32 parenfloor) { dVAR; const int retval = PL_savestack_ix; -#define REGCP_PAREN_ELEMS 4 const int paren_elems_to_push = (PL_regsize - parenfloor) * REGCP_PAREN_ELEMS; + const UV total_elems = paren_elems_to_push + REGCP_OTHER_ELEMS; + const UV elems_shifted = total_elems << SAVE_TIGHT_SHIFT; int p; GET_RE_DEBUG_FLAGS_DECL; if (paren_elems_to_push < 0) Perl_croak(aTHX_ "panic: paren_elems_to_push < 0"); -#define REGCP_OTHER_ELEMS 7 - SSGROW(paren_elems_to_push + REGCP_OTHER_ELEMS); + if ((elems_shifted >> SAVE_TIGHT_SHIFT) != total_elems) + Perl_croak(aTHX_ "panic: paren_elems_to_push offset %"UVuf + " out of range (%d-%d)", total_elems, PL_regsize, parenfloor); + + SSGROW(total_elems + REGCP_FRAME_ELEMS); for (p = PL_regsize; p > parenfloor; p--) { /* REGCP_PARENS_ELEMS are pushed per pairs of parentheses. */ @@ -211,11 +339,7 @@ S_regcppush(pTHX_ I32 parenfloor) SSPUSHINT(*PL_reglastparen); SSPUSHINT(*PL_reglastcloseparen); SSPUSHPTR(PL_reginput); -#define REGCP_FRAME_ELEMS 2 -/* REGCP_FRAME_ELEMS are part of the REGCP_OTHER_ELEMS and - * are needed for the regexp context stack bookkeeping. */ - SSPUSHINT(paren_elems_to_push + REGCP_OTHER_ELEMS - REGCP_FRAME_ELEMS); - SSPUSHINT(SAVEt_REGCONTEXT); /* Magic cookie. */ + SSPUSHUV(SAVEt_REGCONTEXT | elems_shifted); /* Magic cookie. */ return retval; } @@ -240,26 +364,25 @@ STATIC char * S_regcppop(pTHX_ const regexp *rex) { dVAR; - U32 i; + UV i; char *input; GET_RE_DEBUG_FLAGS_DECL; PERL_ARGS_ASSERT_REGCPPOP; /* Pop REGCP_OTHER_ELEMS before the parentheses loop starts. */ - i = SSPOPINT; - assert(i == SAVEt_REGCONTEXT); /* Check that the magic cookie is there. */ - i = SSPOPINT; /* Parentheses elements to pop. */ + i = SSPOPUV; + assert((i & SAVE_MASK) == SAVEt_REGCONTEXT); /* Check that the magic cookie is there. */ + i >>= SAVE_TIGHT_SHIFT; /* Parentheses elements to pop. */ input = (char *) SSPOPPTR; *PL_reglastcloseparen = SSPOPINT; *PL_reglastparen = SSPOPINT; PL_regsize = SSPOPINT; PL_regoffs=(regexp_paren_pair *) SSPOPPTR; - + i -= REGCP_OTHER_ELEMS; /* Now restore the parentheses context. */ - for (i -= (REGCP_OTHER_ELEMS - REGCP_FRAME_ELEMS); - i > 0; i -= REGCP_PAREN_ELEMS) { + for ( ; i > 0; i -= REGCP_PAREN_ELEMS) { I32 tmps; U32 paren = (U32)SSPOPINT; PL_reg_start_tmp[paren] = (char *) SSPOPPTR; @@ -863,9 +986,9 @@ Perl_re_intuit_start(pTHX_ REGEXP * const rx, SV *sv, char *strpos, { /* If flags & SOMETHING - do not do it many times on the same match */ DEBUG_EXECUTE_r(PerlIO_printf(Perl_debug_log, "... Disabling check substring...\n")); + /* XXX Does the destruction order has to change with do_utf8? */ SvREFCNT_dec(do_utf8 ? prog->check_utf8 : prog->check_substr); - if (do_utf8 ? prog->check_substr : prog->check_utf8) - SvREFCNT_dec(do_utf8 ? prog->check_substr : prog->check_utf8); + SvREFCNT_dec(do_utf8 ? prog->check_substr : prog->check_utf8); prog->check_substr = prog->check_utf8 = NULL; /* disable */ prog->float_substr = prog->float_utf8 = NULL; /* clear */ check = NULL; /* abort */ @@ -1007,16 +1130,15 @@ Perl_re_intuit_start(pTHX_ REGEXP * const rx, SV *sv, char *strpos, #define REXEC_TRIE_READ_CHAR(trie_type, trie, widecharmap, uc, uscan, len, \ uvc, charid, foldlen, foldbuf, uniflags) STMT_START { \ - UV uvc_unfolded = 0; \ switch (trie_type) { \ case trie_utf8_fold: \ if ( foldlen>0 ) { \ - uvc_unfolded = uvc = utf8n_to_uvuni( uscan, UTF8_MAXLEN, &len, uniflags ); \ + uvc = utf8n_to_uvuni( uscan, UTF8_MAXLEN, &len, uniflags ); \ foldlen -= len; \ uscan += len; \ len=0; \ } else { \ - uvc_unfolded = uvc = utf8n_to_uvuni( (U8*)uc, UTF8_MAXLEN, &len, uniflags ); \ + uvc = utf8n_to_uvuni( (U8*)uc, UTF8_MAXLEN, &len, uniflags ); \ uvc = to_uni_fold( uvc, foldbuf, &foldlen ); \ foldlen -= UNISKIP( uvc ); \ uscan = foldbuf + UNISKIP( uvc ); \ @@ -1042,7 +1164,6 @@ uvc, charid, foldlen, foldbuf, uniflags) STMT_START { \ uvc = (UV)*uc; \ len = 1; \ } \ - \ if (uvc < 256) { \ charid = trie->charmap[ uvc ]; \ } \ @@ -1055,9 +1176,6 @@ uvc, charid, foldlen, foldbuf, uniflags) STMT_START { \ charid = (U16)SvIV(*svpp); \ } \ } \ - if (!charid && trie_type == trie_utf8_fold && !UTF) { \ - charid = trie->charmap[uvc_unfolded]; \ - } \ } STMT_END #define REXEC_FBC_EXACTISH_CHECK(CoNd) \ @@ -1066,7 +1184,7 @@ uvc, charid, foldlen, foldbuf, uniflags) STMT_START { \ if ( (CoNd) \ && (ln == len || \ !ibcmp_utf8(s, &my_strend, 0, do_utf8, \ - m, NULL, ln, (bool)UTF)) \ + m, NULL, ln, cBOOL(UTF))) \ && (!reginfo || regtry(reginfo, &s)) ) \ goto got_it; \ else { \ @@ -1077,7 +1195,7 @@ uvc, charid, foldlen, foldbuf, uniflags) STMT_START { \ && (f == c1 || f == c2) \ && (ln == len || \ !ibcmp_utf8(s, &my_strend, 0, do_utf8,\ - m, NULL, ln, (bool)UTF)) \ + m, NULL, ln, cBOOL(UTF)))\ && (!reginfo || regtry(reginfo, &s)) ) \ goto got_it; \ } \ @@ -1366,7 +1484,7 @@ S_find_byclass(pTHX_ regexp * prog, const regnode *c, char *s, LOAD_UTF8_CHARCLASS_ALNUM(); REXEC_FBC_UTF8_SCAN( if (tmp == !(OP(c) == BOUND ? - (bool)swash_fetch(PL_utf8_alnum, (U8*)s, do_utf8) : + cBOOL(swash_fetch(PL_utf8_alnum, (U8*)s, do_utf8)) : isALNUM_LC_utf8((U8*)s))) { tmp = !tmp; @@ -1404,7 +1522,7 @@ S_find_byclass(pTHX_ regexp * prog, const regnode *c, char *s, LOAD_UTF8_CHARCLASS_ALNUM(); REXEC_FBC_UTF8_SCAN( if (tmp == !(OP(c) == NBOUND ? - (bool)swash_fetch(PL_utf8_alnum, (U8*)s, do_utf8) : + cBOOL(swash_fetch(PL_utf8_alnum, (U8*)s, do_utf8)) : isALNUM_LC_utf8((U8*)s))) tmp = !tmp; else REXEC_FBC_TRYIT; @@ -1426,8 +1544,8 @@ S_find_byclass(pTHX_ regexp * prog, const regnode *c, char *s, break; case ALNUM: REXEC_FBC_CSCAN_PRELOAD( - LOAD_UTF8_CHARCLASS_ALNUM(), - swash_fetch(PL_utf8_alnum, (U8*)s, do_utf8), + LOAD_UTF8_CHARCLASS_PERL_WORD(), + swash_fetch(RE_utf8_perl_word, (U8*)s, do_utf8), isALNUM(*s) ); case ALNUML: @@ -1437,8 +1555,8 @@ S_find_byclass(pTHX_ regexp * prog, const regnode *c, char *s, ); case NALNUM: REXEC_FBC_CSCAN_PRELOAD( - LOAD_UTF8_CHARCLASS_ALNUM(), - !swash_fetch(PL_utf8_alnum, (U8*)s, do_utf8), + LOAD_UTF8_CHARCLASS_PERL_WORD(), + !swash_fetch(RE_utf8_perl_word, (U8*)s, do_utf8), !isALNUM(*s) ); case NALNUML: @@ -1448,8 +1566,8 @@ S_find_byclass(pTHX_ regexp * prog, const regnode *c, char *s, ); case SPACE: REXEC_FBC_CSCAN_PRELOAD( - LOAD_UTF8_CHARCLASS_SPACE(), - *s == ' ' || swash_fetch(PL_utf8_space,(U8*)s, do_utf8), + LOAD_UTF8_CHARCLASS_PERL_SPACE(), + *s == ' ' || swash_fetch(RE_utf8_perl_space,(U8*)s, do_utf8), isSPACE(*s) ); case SPACEL: @@ -1459,8 +1577,8 @@ S_find_byclass(pTHX_ regexp * prog, const regnode *c, char *s, ); case NSPACE: REXEC_FBC_CSCAN_PRELOAD( - LOAD_UTF8_CHARCLASS_SPACE(), - !(*s == ' ' || swash_fetch(PL_utf8_space,(U8*)s, do_utf8)), + LOAD_UTF8_CHARCLASS_PERL_SPACE(), + !(*s == ' ' || swash_fetch(RE_utf8_perl_space,(U8*)s, do_utf8)), !isSPACE(*s) ); case NSPACEL: @@ -1470,8 +1588,8 @@ S_find_byclass(pTHX_ regexp * prog, const regnode *c, char *s, ); case DIGIT: REXEC_FBC_CSCAN_PRELOAD( - LOAD_UTF8_CHARCLASS_DIGIT(), - swash_fetch(PL_utf8_digit,(U8*)s, do_utf8), + LOAD_UTF8_CHARCLASS_POSIX_DIGIT(), + swash_fetch(RE_utf8_posix_digit,(U8*)s, do_utf8), isDIGIT(*s) ); case DIGITL: @@ -1481,8 +1599,8 @@ S_find_byclass(pTHX_ regexp * prog, const regnode *c, char *s, ); case NDIGIT: REXEC_FBC_CSCAN_PRELOAD( - LOAD_UTF8_CHARCLASS_DIGIT(), - !swash_fetch(PL_utf8_digit,(U8*)s, do_utf8), + LOAD_UTF8_CHARCLASS_POSIX_DIGIT(), + !swash_fetch(RE_utf8_posix_digit,(U8*)s, do_utf8), !isDIGIT(*s) ); case NDIGITL: @@ -1618,7 +1736,7 @@ S_find_byclass(pTHX_ regexp * prog, const regnode *c, char *s, } if ( word ) { - U8 *lpos= points[ (pointpos - trie->wordlen[word-1] ) % maxlen ]; + U8 *lpos= points[ (pointpos - trie->wordinfo[word].len) % maxlen ]; if (!leftmost || lpos < leftmost) { DEBUG_r(accepted_word=word); leftmost= lpos; @@ -1692,7 +1810,7 @@ S_find_byclass(pTHX_ regexp * prog, const regnode *c, char *s, } } if ( aho->states[ state ].wordnum ) { - U8 *lpos = points[ (pointpos - trie->wordlen[aho->states[ state ].wordnum-1]) % maxlen ]; + U8 *lpos = points[ (pointpos - trie->wordinfo[aho->states[ state ].wordnum].len) % maxlen ]; if (!leftmost || lpos < leftmost) { DEBUG_r(accepted_word=aho->states[ state ].wordnum); leftmost = lpos; @@ -1759,7 +1877,7 @@ Perl_regexec_flags(pTHX_ REGEXP * const rx, char *stringarg, register char *stre I32 end_shift = 0; /* Same for the end. */ /* CC */ I32 scream_pos = -1; /* Internal iterator of scream. */ char *scream_olds = NULL; - const bool do_utf8 = (bool)DO_UTF8(sv); + const bool do_utf8 = cBOOL(DO_UTF8(sv)); I32 multiline; RXi_GET_DECL(prog,progi); regmatch_info reginfo; /* create some info to pass to regtry etc */ @@ -2387,9 +2505,6 @@ S_regtry(pTHX_ regmatch_info *reginfo, char **startpos) #define REPORT_CODE_OFF 32 -/* Make sure there is a test for this +1 options in re_tests */ -#define TRIE_INITAL_ACCEPT_BUFFLEN 4; - #define CHRTEST_UNINIT -1001 /* c1/c2 haven't been calculated yet */ #define CHRTEST_VOID -1000 /* the c1/c2 "next char" test should be skipped */ @@ -2951,6 +3066,50 @@ S_regmatch(pTHX_ regmatch_info *reginfo, regnode *prog) } /* FALL THROUGH */ case TRIE: + /* the basic plan of execution of the trie is: + * At the beginning, run though all the states, and + * find the longest-matching word. Also remember the position + * of the shortest matching word. For example, this pattern: + * 1 2 3 4 5 + * ab|a|x|abcd|abc + * when matched against the string "abcde", will generate + * accept states for all words except 3, with the longest + * matching word being 4, and the shortest being 1 (with + * the position being after char 1 of the string). + * + * Then for each matching word, in word order (i.e. 1,2,4,5), + * we run the remainder of the pattern; on each try setting + * the current position to the character following the word, + * returning to try the next word on failure. + * + * We avoid having to build a list of words at runtime by + * using a compile-time structure, wordinfo[].prev, which + * gives, for each word, the previous accepting word (if any). + * In the case above it would contain the mappings 1->2, 2->0, + * 3->0, 4->5, 5->1. We can use this table to generate, from + * the longest word (4 above), a list of all words, by + * following the list of prev pointers; this gives us the + * unordered list 4,5,1,2. Then given the current word we have + * just tried, we can go through the list and find the + * next-biggest word to try (so if we just failed on word 2, + * the next in the list is 4). + * + * Since at runtime we don't record the matching position in + * the string for each word, we have to work that out for + * each word we're about to process. The wordinfo table holds + * the character length of each word; given that we recorded + * at the start: the position of the shortest word and its + * length in chars, we just need to move the pointer the + * difference between the two char lengths. Depending on + * Unicode status and folding, that's cheap or expensive. + * + * This algorithm is optimised for the case where are only a + * small number of accept states, i.e. 0,1, or maybe 2. + * With lots of accepts states, and having to try all of them, + * it becomes quadratic on number of accept states to find all + * the next words. + */ + { /* what type of TRIE am I? (utf8 makes this contextual) */ DECL_TRIE_TYPE(scan); @@ -2987,76 +3146,62 @@ S_regmatch(pTHX_ regmatch_info *reginfo, regnode *prog) STRLEN len = 0; STRLEN foldlen = 0; U8 *uscan = (U8*)NULL; - STRLEN bufflen=0; - SV *sv_accept_buff = NULL; U8 foldbuf[ UTF8_MAXBYTES_CASE + 1 ]; + U32 charcount = 0; /* how many input chars we have matched */ + U32 accepted = 0; /* have we seen any accepting states? */ - ST.accepted = 0; /* how many accepting states we have seen */ ST.B = next; ST.jump = trie->jump; ST.me = scan; - /* - traverse the TRIE keeping track of all accepting states - we transition through until we get to a failing node. - */ + ST.firstpos = NULL; + ST.longfold = FALSE; /* char longer if folded => it's harder */ + ST.nextword = 0; + + /* fully traverse the TRIE; note the position of the + shortest accept state and the wordnum of the longest + accept state */ while ( state && uc <= (U8*)PL_regeol ) { U32 base = trie->states[ state ].trans.base; UV uvc = 0; U16 charid; - /* We use charid to hold the wordnum as we don't use it - for charid until after we have done the wordnum logic. - We define an alias just so that the wordnum logic reads - more naturally. */ - -#define got_wordnum charid - got_wordnum = trie->states[ state ].wordnum; - - if ( got_wordnum ) { - if ( ! ST.accepted ) { - ENTER; - SAVETMPS; /* XXX is this necessary? dmq */ - bufflen = TRIE_INITAL_ACCEPT_BUFFLEN; - sv_accept_buff=newSV(bufflen * - sizeof(reg_trie_accepted) - 1); - SvCUR_set(sv_accept_buff, 0); - SvPOK_on(sv_accept_buff); - sv_2mortal(sv_accept_buff); - SAVETMPS; - ST.accept_buff = - (reg_trie_accepted*)SvPV_nolen(sv_accept_buff ); - } - do { - if (ST.accepted >= bufflen) { - bufflen *= 2; - ST.accept_buff =(reg_trie_accepted*) - SvGROW(sv_accept_buff, - bufflen * sizeof(reg_trie_accepted)); + U16 wordnum; + wordnum = trie->states[ state ].wordnum; + + if (wordnum) { /* it's an accept state */ + if (!accepted) { + accepted = 1; + /* record first match position */ + if (ST.longfold) { + ST.firstpos = (U8*)locinput; + ST.firstchars = 0; } - SvCUR_set(sv_accept_buff,SvCUR(sv_accept_buff) - + sizeof(reg_trie_accepted)); - - - ST.accept_buff[ST.accepted].wordnum = got_wordnum; - ST.accept_buff[ST.accepted].endpos = uc; - ++ST.accepted; - } while (trie->nextword && (got_wordnum= trie->nextword[got_wordnum])); + else { + ST.firstpos = uc; + ST.firstchars = charcount; + } + } + if (!ST.nextword || wordnum < ST.nextword) + ST.nextword = wordnum; + ST.topword = wordnum; } -#undef got_wordnum DEBUG_TRIE_EXECUTE_r({ DUMP_EXEC_POS( (char *)uc, scan, do_utf8 ); PerlIO_printf( Perl_debug_log, - "%*s %sState: %4"UVxf" Accepted: %4"UVxf" ", + "%*s %sState: %4"UVxf" Accepted: %c ", 2+depth * 2, "", PL_colors[4], - (UV)state, (UV)ST.accepted ); + (UV)state, (accepted ? 'Y' : 'N')); }); + /* read a char and goto next state */ if ( base ) { REXEC_TRIE_READ_CHAR(trie_type, trie, widecharmap, uc, uscan, len, uvc, charid, foldlen, foldbuf, uniflags); - + charcount++; + if (foldlen>0) + ST.longfold = TRUE; if (charid && (base + charid > trie->uniquecharcount ) && (base + charid - 1 - trie->uniquecharcount @@ -3082,77 +3227,38 @@ S_regmatch(pTHX_ regmatch_info *reginfo, regnode *prog) charid, uvc, (UV)state, PL_colors[5] ); ); } - if (!ST.accepted ) + if (!accepted) sayNO; + /* calculate total number of accept states */ + { + U16 w = ST.topword; + accepted = 0; + while (w) { + w = trie->wordinfo[w].prev; + accepted++; + } + ST.accepted = accepted; + } + DEBUG_EXECUTE_r( PerlIO_printf( Perl_debug_log, "%*s %sgot %"IVdf" possible matches%s\n", REPORT_CODE_OFF + depth * 2, "", PL_colors[4], (IV)ST.accepted, PL_colors[5] ); ); + goto trie_first_try; /* jump into the fail handler */ }} - goto trie_first_try; /* jump into the fail handler */ /* NOTREACHED */ - case TRIE_next_fail: /* we failed - try next alterative */ + + case TRIE_next_fail: /* we failed - try next alternative */ if ( ST.jump) { REGCP_UNWIND(ST.cp); for (n = *PL_reglastparen; n > ST.lastparen; n--) PL_regoffs[n].end = -1; *PL_reglastparen = n; } - trie_first_try: - if (do_cutgroup) { - do_cutgroup = 0; - no_final = 0; - } - - if ( ST.jump) { - ST.lastparen = *PL_reglastparen; - REGCP_SET(ST.cp); - } - if ( ST.accepted == 1 ) { - /* only one choice left - just continue */ - DEBUG_EXECUTE_r({ - AV *const trie_words - = MUTABLE_AV(rexi->data->data[ARG(ST.me)+TRIE_WORDS_OFFSET]); - SV ** const tmp = av_fetch( trie_words, - ST.accept_buff[ 0 ].wordnum-1, 0 ); - SV *sv= tmp ? sv_newmortal() : NULL; - - PerlIO_printf( Perl_debug_log, - "%*s %sonly one match left: #%d <%s>%s\n", - REPORT_CODE_OFF+depth*2, "", PL_colors[4], - ST.accept_buff[ 0 ].wordnum, - tmp ? pv_pretty(sv, SvPV_nolen_const(*tmp), SvCUR(*tmp), 0, - PL_colors[0], PL_colors[1], - (SvUTF8(*tmp) ? PERL_PV_ESCAPE_UNI : 0) - ) - : "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. */ - - locinput = PL_reginput; - nextchr = UCHARAT(locinput); - if ( !ST.jump || !ST.jump[ST.accept_buff[0].wordnum]) - scan = ST.B; - else - scan = ST.me + ST.jump[ST.accept_buff[0].wordnum]; - if (!has_cutgroup) { - FREETMPS; - LEAVE; - } else { - ST.accepted--; - PUSH_YES_STATE_GOTO(TRIE_next, scan); - } - - continue; /* execute rest of RE */ - } - - if ( !ST.accepted-- ) { + if (!--ST.accepted) { DEBUG_EXECUTE_r({ PerlIO_printf( Perl_debug_log, "%*s %sTRIE failed...%s\n", @@ -3160,86 +3266,129 @@ S_regmatch(pTHX_ regmatch_info *reginfo, regnode *prog) PL_colors[4], PL_colors[5] ); }); - FREETMPS; - LEAVE; sayNO_SILENT; - /*NOTREACHED*/ - } + } + { + /* Find next-highest word to process. Note that this code + * is O(N^2) per trie run (O(N) per branch), so keep tight */ + register U16 min = 0; + register U16 word; + register U16 const nextword = ST.nextword; + register reg_trie_wordinfo * const wordinfo + = ((reg_trie_data*)rexi->data->data[ARG(ST.me)])->wordinfo; + for (word=ST.topword; word; word=wordinfo[word].prev) { + if (word > nextword && (!min || word < min)) + min = word; + } + ST.nextword = min; + } - /* - 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 - */ + trie_first_try: + if (do_cutgroup) { + do_cutgroup = 0; + no_final = 0; + } - { - U32 best = 0; - U32 cur; - for( cur = 1 ; cur <= ST.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 + depth * 2, "", PL_colors[4], - (IV)best, ST.accept_buff[ best ].wordnum, (IV)cur, - ST.accept_buff[ cur ].wordnum, PL_colors[5] ); - ); + if ( ST.jump) { + ST.lastparen = *PL_reglastparen; + REGCP_SET(ST.cp); + } - if (ST.accept_buff[cur].wordnum < - ST.accept_buff[best].wordnum) - best = cur; + /* find start char of end of current word */ + { + U32 chars; /* how many chars to skip */ + U8 *uc = ST.firstpos; + reg_trie_data * const trie + = (reg_trie_data*)rexi->data->data[ARG(ST.me)]; + + assert((trie->wordinfo[ST.nextword].len - trie->prefixlen) + >= ST.firstchars); + chars = (trie->wordinfo[ST.nextword].len - trie->prefixlen) + - ST.firstchars; + + if (ST.longfold) { + /* the hard option - fold each char in turn and find + * its folded length (which may be different */ + U8 foldbuf[UTF8_MAXBYTES_CASE + 1]; + STRLEN foldlen; + STRLEN len; + UV uvc; + U8 *uscan; + + while (chars) { + if (do_utf8) { + uvc = utf8n_to_uvuni((U8*)uc, UTF8_MAXLEN, &len, + uniflags); + uc += len; + } + else { + uvc = *uc; + uc++; + } + uvc = to_uni_fold(uvc, foldbuf, &foldlen); + uscan = foldbuf; + while (foldlen) { + if (!--chars) + break; + uvc = utf8n_to_uvuni(uscan, UTF8_MAXLEN, &len, + uniflags); + uscan += len; + foldlen -= len; + } + } + } + else { + if (do_utf8) + while (chars--) + uc += UTF8SKIP(uc); + else + uc += chars; } + PL_reginput = (char *)uc; + } - DEBUG_EXECUTE_r({ - AV *const trie_words - = MUTABLE_AV(rexi->data->data[ARG(ST.me)+TRIE_WORDS_OFFSET]); - SV ** const tmp = av_fetch( trie_words, - ST.accept_buff[ best ].wordnum - 1, 0 ); - regnode *nextop=(!ST.jump || !ST.jump[ST.accept_buff[best].wordnum]) ? - ST.B : - ST.me + ST.jump[ST.accept_buff[best].wordnum]; - SV *sv= tmp ? sv_newmortal() : NULL; - - PerlIO_printf( Perl_debug_log, - "%*s %strying alternation #%d <%s> at node #%d %s\n", - REPORT_CODE_OFF+depth*2, "", PL_colors[4], - ST.accept_buff[best].wordnum, - tmp ? pv_pretty(sv, SvPV_nolen_const(*tmp), SvCUR(*tmp), 0, - PL_colors[0], PL_colors[1], - (SvUTF8(*tmp) ? PERL_PV_ESCAPE_UNI : 0) - ) : "not compiled under -Dr", - REG_NODE_NUM(nextop), - PL_colors[5] ); - }); + scan = (ST.jump && ST.jump[ST.nextword]) + ? ST.me + ST.jump[ST.nextword] + : ST.B; - if ( best 1 || has_cutgroup) { + PUSH_STATE_GOTO(TRIE_next, scan); + /* NOTREACHED */ } + /* only one choice left - just continue */ + DEBUG_EXECUTE_r({ + AV *const trie_words + = MUTABLE_AV(rexi->data->data[ARG(ST.me)+TRIE_WORDS_OFFSET]); + SV ** const tmp = av_fetch( trie_words, + ST.nextword-1, 0 ); + SV *sv= tmp ? sv_newmortal() : NULL; + + PerlIO_printf( Perl_debug_log, + "%*s %sonly one match left, short-circuiting: #%d <%s>%s\n", + REPORT_CODE_OFF+depth*2, "", PL_colors[4], + ST.nextword, + tmp ? pv_pretty(sv, SvPV_nolen_const(*tmp), SvCUR(*tmp), 0, + PL_colors[0], PL_colors[1], + (SvUTF8(*tmp) ? PERL_PV_ESCAPE_UNI : 0) + ) + : "not compiled under -Dr", + PL_colors[5] ); + }); + + locinput = PL_reginput; + nextchr = UCHARAT(locinput); + continue; /* execute rest of RE */ /* NOTREACHED */ - case TRIE_next: - /* we dont want to throw this away, see bug 57042*/ - if (oreplsv != GvSV(PL_replgv)) - sv_setsv(oreplsv, GvSV(PL_replgv)); - FREETMPS; - LEAVE; - sayYES; #undef ST case EXACT: { @@ -3306,7 +3455,7 @@ S_regmatch(pTHX_ regmatch_info *reginfo, regnode *prog) const char * const l = locinput; char *e = PL_regeol; - if (ibcmp_utf8(s, 0, ln, (bool)UTF, + if (ibcmp_utf8(s, 0, ln, cBOOL(UTF), l, &e, 0, do_utf8)) { /* One more case for the sharp s: * pack("U0U*", 0xDF) =~ /ss/i, @@ -3344,85 +3493,6 @@ S_regmatch(pTHX_ regmatch_info *reginfo, regnode *prog) nextchr = UCHARAT(locinput); break; } - case ANYOF: - if (do_utf8) { - STRLEN inclasslen = PL_regeol - locinput; - - if (!reginclass(rex, scan, (U8*)locinput, &inclasslen, do_utf8)) - goto anyof_fail; - if (locinput >= PL_regeol) - sayNO; - locinput += inclasslen ? inclasslen : UTF8SKIP(locinput); - nextchr = UCHARAT(locinput); - break; - } - else { - if (nextchr < 0) - nextchr = UCHARAT(locinput); - if (!REGINCLASS(rex, scan, (U8*)locinput)) - goto anyof_fail; - if (!nextchr && locinput >= PL_regeol) - sayNO; - nextchr = UCHARAT(++locinput); - break; - } - anyof_fail: - /* If we might have the case of the German sharp s - * in a casefolding Unicode character class. */ - - if (ANYOF_FOLD_SHARP_S(scan, locinput, PL_regeol)) { - locinput += SHARP_S_SKIP; - nextchr = UCHARAT(locinput); - } - else - sayNO; - break; - case ALNUML: - PL_reg_flags |= RF_tainted; - /* FALL THROUGH */ - case ALNUM: - if (!nextchr) - sayNO; - if (do_utf8) { - LOAD_UTF8_CHARCLASS_ALNUM(); - if (!(OP(scan) == ALNUM - ? (bool)swash_fetch(PL_utf8_alnum, (U8*)locinput, do_utf8) - : isALNUM_LC_utf8((U8*)locinput))) - { - sayNO; - } - locinput += PL_utf8skip[nextchr]; - nextchr = UCHARAT(locinput); - break; - } - if (!(OP(scan) == ALNUM - ? isALNUM(nextchr) : isALNUM_LC(nextchr))) - sayNO; - nextchr = UCHARAT(++locinput); - break; - case NALNUML: - PL_reg_flags |= RF_tainted; - /* FALL THROUGH */ - case NALNUM: - if (!nextchr && locinput >= PL_regeol) - sayNO; - if (do_utf8) { - LOAD_UTF8_CHARCLASS_ALNUM(); - if (OP(scan) == NALNUM - ? (bool)swash_fetch(PL_utf8_alnum, (U8*)locinput, do_utf8) - : isALNUM_LC_utf8((U8*)locinput)) - { - sayNO; - } - locinput += PL_utf8skip[nextchr]; - nextchr = UCHARAT(locinput); - break; - } - if (OP(scan) == NALNUM - ? isALNUM(nextchr) : isALNUM_LC(nextchr)) - sayNO; - nextchr = UCHARAT(++locinput); - break; case BOUNDL: case NBOUNDL: PL_reg_flags |= RF_tainted; @@ -3435,7 +3505,7 @@ S_regmatch(pTHX_ regmatch_info *reginfo, regnode *prog) ln = '\n'; else { const U8 * const r = reghop3((U8*)locinput, -1, (U8*)PL_bostr); - + ln = utf8n_to_uvchr(r, UTF8SKIP(r), 0, uniflags); } if (OP(scan) == BOUND || OP(scan) == NBOUND) { @@ -3464,116 +3534,259 @@ S_regmatch(pTHX_ regmatch_info *reginfo, regnode *prog) OP(scan) == BOUNDL)) sayNO; break; - case SPACEL: - PL_reg_flags |= RF_tainted; - /* FALL THROUGH */ - case SPACE: - if (!nextchr) - sayNO; - if (do_utf8) { - if (UTF8_IS_CONTINUED(nextchr)) { - LOAD_UTF8_CHARCLASS_SPACE(); - if (!(OP(scan) == SPACE - ? (bool)swash_fetch(PL_utf8_space, (U8*)locinput, do_utf8) - : isSPACE_LC_utf8((U8*)locinput))) - { - sayNO; - } - locinput += PL_utf8skip[nextchr]; - nextchr = UCHARAT(locinput); - break; - } - } - if (!(OP(scan) == SPACE - ? isSPACE(nextchr) : isSPACE_LC(nextchr))) - sayNO; - nextchr = UCHARAT(++locinput); - break; - case NSPACEL: - PL_reg_flags |= RF_tainted; - /* FALL THROUGH */ - case NSPACE: - if (!nextchr && locinput >= PL_regeol) - sayNO; + case ANYOF: if (do_utf8) { - LOAD_UTF8_CHARCLASS_SPACE(); - if (OP(scan) == NSPACE - ? (bool)swash_fetch(PL_utf8_space, (U8*)locinput, do_utf8) - : isSPACE_LC_utf8((U8*)locinput)) - { + STRLEN inclasslen = PL_regeol - locinput; + + if (!reginclass(rex, scan, (U8*)locinput, &inclasslen, do_utf8)) + goto anyof_fail; + if (locinput >= PL_regeol) sayNO; - } - locinput += PL_utf8skip[nextchr]; + locinput += inclasslen ? inclasslen : UTF8SKIP(locinput); nextchr = UCHARAT(locinput); break; } - if (OP(scan) == NSPACE - ? isSPACE(nextchr) : isSPACE_LC(nextchr)) - sayNO; - nextchr = UCHARAT(++locinput); - break; - case DIGITL: - PL_reg_flags |= RF_tainted; - /* FALL THROUGH */ - case DIGIT: - if (!nextchr) - sayNO; - if (do_utf8) { - LOAD_UTF8_CHARCLASS_DIGIT(); - if (!(OP(scan) == DIGIT - ? (bool)swash_fetch(PL_utf8_digit, (U8*)locinput, do_utf8) - : isDIGIT_LC_utf8((U8*)locinput))) - { + else { + if (nextchr < 0) + nextchr = UCHARAT(locinput); + if (!REGINCLASS(rex, scan, (U8*)locinput)) + goto anyof_fail; + if (!nextchr && locinput >= PL_regeol) sayNO; - } - locinput += PL_utf8skip[nextchr]; - nextchr = UCHARAT(locinput); + nextchr = UCHARAT(++locinput); break; } - if (!(OP(scan) == DIGIT - ? isDIGIT(nextchr) : isDIGIT_LC(nextchr))) - sayNO; - nextchr = UCHARAT(++locinput); - break; - case NDIGITL: - PL_reg_flags |= RF_tainted; - /* FALL THROUGH */ - case NDIGIT: - if (!nextchr && locinput >= PL_regeol) - sayNO; - if (do_utf8) { - LOAD_UTF8_CHARCLASS_DIGIT(); - if (OP(scan) == NDIGIT - ? (bool)swash_fetch(PL_utf8_digit, (U8*)locinput, do_utf8) - : isDIGIT_LC_utf8((U8*)locinput)) - { - sayNO; - } - locinput += PL_utf8skip[nextchr]; - nextchr = UCHARAT(locinput); - break; + anyof_fail: + /* If we might have the case of the German sharp s + * in a casefolding Unicode character class. */ + + if (ANYOF_FOLD_SHARP_S(scan, locinput, PL_regeol)) { + locinput += SHARP_S_SKIP; + nextchr = UCHARAT(locinput); } - if (OP(scan) == NDIGIT - ? isDIGIT(nextchr) : isDIGIT_LC(nextchr)) - sayNO; - nextchr = UCHARAT(++locinput); + else + sayNO; break; - case CLUMP: + /* Special char classes - The defines start on line 129 or so */ + CCC_TRY_AFF( ALNUM, ALNUML, perl_word, "a", isALNUM_LC_utf8, isALNUM, isALNUM_LC); + CCC_TRY_NEG(NALNUM, NALNUML, perl_word, "a", isALNUM_LC_utf8, isALNUM, isALNUM_LC); + + CCC_TRY_AFF( SPACE, SPACEL, perl_space, " ", isSPACE_LC_utf8, isSPACE, isSPACE_LC); + CCC_TRY_NEG(NSPACE, NSPACEL, perl_space, " ", isSPACE_LC_utf8, isSPACE, isSPACE_LC); + + CCC_TRY_AFF( DIGIT, DIGITL, posix_digit, "0", isDIGIT_LC_utf8, isDIGIT, isDIGIT_LC); + CCC_TRY_NEG(NDIGIT, NDIGITL, posix_digit, "0", isDIGIT_LC_utf8, isDIGIT, isDIGIT_LC); + + case CLUMP: /* Match \X: logical Unicode character. This is defined as + a Unicode extended Grapheme Cluster */ + /* From http://www.unicode.org/reports/tr29 (5.2 version). An + extended Grapheme Cluster is: + + CR LF + | Prepend* Begin Extend* + | . + + Begin is (Hangul-syllable | ! Control) + Extend is (Grapheme_Extend | Spacing_Mark) + Control is [ GCB_Control CR LF ] + + The discussion below shows how the code for CLUMP is derived + from this regex. Note that most of these concepts are from + property values of the Grapheme Cluster Boundary (GCB) property. + No code point can have multiple property values for a given + property. Thus a code point in Prepend can't be in Control, but + it must be in !Control. This is why Control above includes + GCB_Control plus CR plus LF. The latter two are used in the GCB + property separately, and so can't be in GCB_Control, even though + they logically are controls. Control is not the same as gc=cc, + but includes format and other characters as well. + + The Unicode definition of Hangul-syllable is: + L+ + | (L* ( ( V | LV ) V* | LVT ) T*) + | T+ + ) + Each of these is a value for the GCB property, and hence must be + disjoint, so the order they are tested is immaterial, so the + above can safely be changed to + T+ + | L+ + | (L* ( LVT | ( V | LV ) V*) T*) + + The last two terms can be combined like this: + L* ( L + | (( LVT | ( V | LV ) V*) T*)) + + And refactored into this: + L* (L | LVT T* | V V* T* | LV V* T*) + + That means that if we have seen any L's at all we can quit + there, but if the next character is a LVT, a V or and LV we + should keep going. + + There is a subtlety with Prepend* which showed up in testing. + Note that the Begin, and only the Begin is required in: + | Prepend* Begin Extend* + Also, Begin contains '! Control'. A Prepend must be a '! + Control', which means it must be a Begin. What it comes down to + is that if we match Prepend* and then find no suitable Begin + afterwards, that if we backtrack the last Prepend, that one will + be a suitable Begin. + */ + if (locinput >= PL_regeol) sayNO; - if (do_utf8) { - LOAD_UTF8_CHARCLASS_MARK(); - if (swash_fetch(PL_utf8_mark,(U8*)locinput, do_utf8)) - sayNO; - locinput += PL_utf8skip[nextchr]; - while (locinput < PL_regeol && - swash_fetch(PL_utf8_mark,(U8*)locinput, do_utf8)) - locinput += UTF8SKIP(locinput); - if (locinput > PL_regeol) - sayNO; - } - else - locinput++; + if (! do_utf8) { + + /* Match either CR LF or '.', as all the other possibilities + * require utf8 */ + locinput++; /* Match the . or CR */ + if (nextchr == '\r' + && locinput < PL_regeol + && UCHARAT(locinput) == '\n') locinput++; + } + else { + + /* Utf8: See if is ( CR LF ); already know that locinput < + * PL_regeol, so locinput+1 is in bounds */ + if (nextchr == '\r' && UCHARAT(locinput + 1) == '\n') { + locinput += 2; + } + else { + /* In case have to backtrack to beginning, then match '.' */ + char *starting = locinput; + + /* In case have to backtrack the last prepend */ + char *previous_prepend = 0; + + LOAD_UTF8_CHARCLASS_GCB(); + + /* Match (prepend)* */ + while (locinput < PL_regeol + && swash_fetch(PL_utf8_X_prepend, + (U8*)locinput, do_utf8)) + { + previous_prepend = locinput; + locinput += UTF8SKIP(locinput); + } + + /* As noted above, if we matched a prepend character, but + * the next thing won't match, back off the last prepend we + * matched, as it is guaranteed to match the begin */ + if (previous_prepend + && (locinput >= PL_regeol + || ! swash_fetch(PL_utf8_X_begin, + (U8*)locinput, do_utf8))) + { + locinput = previous_prepend; + } + + /* Note that here we know PL_regeol > locinput, as we + * tested that upon input to this switch case, and if we + * moved locinput forward, we tested the result just above + * and it either passed, or we backed off so that it will + * now pass */ + if (! swash_fetch(PL_utf8_X_begin, (U8*)locinput, do_utf8)) { + + /* Here did not match the required 'Begin' in the + * second term. So just match the very first + * character, the '.' of the final term of the regex */ + locinput = starting + UTF8SKIP(starting); + } else { + + /* Here is the beginning of a character that can have + * an extender. It is either a hangul syllable, or a + * non-control */ + if (swash_fetch(PL_utf8_X_non_hangul, + (U8*)locinput, do_utf8)) + { + + /* Here not a Hangul syllable, must be a + * ('! * Control') */ + locinput += UTF8SKIP(locinput); + } else { + + /* Here is a Hangul syllable. It can be composed + * of several individual characters. One + * possibility is T+ */ + if (swash_fetch(PL_utf8_X_T, + (U8*)locinput, do_utf8)) + { + while (locinput < PL_regeol + && swash_fetch(PL_utf8_X_T, + (U8*)locinput, do_utf8)) + { + locinput += UTF8SKIP(locinput); + } + } else { + + /* Here, not T+, but is a Hangul. That means + * it is one of the others: L, LV, LVT or V, + * and matches: + * L* (L | LVT T* | V V* T* | LV V* T*) */ + + /* Match L* */ + while (locinput < PL_regeol + && swash_fetch(PL_utf8_X_L, + (U8*)locinput, do_utf8)) + { + locinput += UTF8SKIP(locinput); + } + + /* Here, have exhausted L*. If the next + * character is not an LV, LVT nor V, it means + * we had to have at least one L, so matches L+ + * in the original equation, we have a complete + * hangul syllable. Are done. */ + + if (locinput < PL_regeol + && swash_fetch(PL_utf8_X_LV_LVT_V, + (U8*)locinput, do_utf8)) + { + + /* Otherwise keep going. Must be LV, LVT + * or V. See if LVT */ + if (swash_fetch(PL_utf8_X_LVT, + (U8*)locinput, do_utf8)) + { + locinput += UTF8SKIP(locinput); + } else { + + /* Must be V or LV. Take it, then + * match V* */ + locinput += UTF8SKIP(locinput); + while (locinput < PL_regeol + && swash_fetch(PL_utf8_X_V, + (U8*)locinput, do_utf8)) + { + locinput += UTF8SKIP(locinput); + } + } + + /* And any of LV, LVT, or V can be followed + * by T* */ + while (locinput < PL_regeol + && swash_fetch(PL_utf8_X_T, + (U8*)locinput, + do_utf8)) + { + locinput += UTF8SKIP(locinput); + } + } + } + } + + /* Match any extender */ + while (locinput < PL_regeol + && swash_fetch(PL_utf8_X_extend, + (U8*)locinput, do_utf8)) + { + locinput += UTF8SKIP(locinput); + } + } + } + if (locinput > PL_regeol) sayNO; + } nextchr = UCHARAT(locinput); break; @@ -3787,7 +4000,7 @@ S_regmatch(pTHX_ regmatch_info *reginfo, regnode *prog) assert(rx); } if (rx) { - rx = reg_temp_copy(rx); + rx = reg_temp_copy(NULL, rx); } else { U32 pm_flags = 0; @@ -3878,7 +4091,7 @@ S_regmatch(pTHX_ regmatch_info *reginfo, regnode *prog) /* NOTREACHED */ } /* logical is 1, /(?(?{...})X|Y)/ */ - sw = (bool)SvTRUE(ret); + sw = cBOOL(SvTRUE(ret)); logical = 0; break; } @@ -3979,11 +4192,11 @@ S_regmatch(pTHX_ regmatch_info *reginfo, regnode *prog) /*NOTREACHED*/ case GROUPP: n = ARG(scan); /* which paren pair */ - sw = (bool)(*PL_reglastparen >= n && PL_regoffs[n].end != -1); + sw = cBOOL(*PL_reglastparen >= n && PL_regoffs[n].end != -1); break; case NGROUPP: /* reg_check_named_buff_matched returns 0 for no match */ - sw = (bool)(0 < reg_check_named_buff_matched(rex,scan)); + sw = cBOOL(0 < reg_check_named_buff_matched(rex,scan)); break; case INSUBP: n = ARG(scan); @@ -4990,7 +5203,7 @@ NULL /* trivial fail */ if (logical) { logical = 0; - sw = 1 - (bool)ST.wanted; + sw = 1 - cBOOL(ST.wanted); } else if (ST.wanted) sayNO; @@ -5019,7 +5232,7 @@ NULL case IFMATCH_A: /* body of (?...A) succeeded */ if (ST.logical) { - sw = (bool)ST.wanted; + sw = cBOOL(ST.wanted); } else if (!ST.wanted) sayNO; @@ -5771,8 +5984,10 @@ S_reginclass(pTHX_ const regexp *prog, register const regnode *n, register const if (do_utf8 && !UTF8_IS_INVARIANT(c)) { c = utf8n_to_uvchr(p, UTF8_MAXBYTES, &len, - (UTF8_ALLOW_DEFAULT & UTF8_ALLOW_ANYUV) | UTF8_CHECK_ONLY); - /* see [perl #37836] for UTF8_ALLOW_ANYUV */ + (UTF8_ALLOW_DEFAULT & UTF8_ALLOW_ANYUV) + | UTF8_ALLOW_FFFF | UTF8_CHECK_ONLY); + /* see [perl #37836] for UTF8_ALLOW_ANYUV; [perl #38293] for + * UTF8_ALLOW_FFFF */ if (len == (STRLEN)-1) Perl_croak(aTHX_ "Malformed UTF-8 character (fatal)"); }