diff options
author | Karl Williamson <public@khwilliamson.com> | 2014-01-27 15:35:00 -0700 |
---|---|---|
committer | Karl Williamson <public@khwilliamson.com> | 2014-01-27 23:03:48 -0700 |
commit | 31f05a37c4e9c37a7263491f2fc0237d836e1a80 (patch) | |
tree | 7537c7e179350243b3de0f3a99d6747c9c7812e6 /regcomp.h | |
parent | cea315b64e0c4b1890867df0c925cafc8823ba38 (diff) | |
download | perl-31f05a37c4e9c37a7263491f2fc0237d836e1a80.tar.gz |
Work properly under UTF-8 LC_CTYPE locales
This large (sorry, I couldn't figure out how to meaningfully split it
up) commit causes Perl to fully support LC_CTYPE operations (case
changing, character classification) in UTF-8 locales.
As a side effect it resolves [perl #56820].
The basics are easy, but there were a lot of details, and one
troublesome edge case discussed below.
What essentially happens is that when the locale is changed to a UTF-8
one, a global variable is set TRUE (FALSE when changed to a non-UTF-8
locale). Within the scope of 'use locale', this variable is checked,
and if TRUE, the code that Perl uses for non-locale behavior is used
instead of the code for locale behavior. Since Perl's internal
representation is UTF-8, we get UTF-8 behavior for a UTF-8 locale.
More work had to be done for regular expressions. There are three
cases.
1) The character classes \w, [[:punct:]] needed no extra work, as
the changes fall out from the base work.
2) Strings that are to be matched case-insensitively. These form
EXACTFL regops (nodes). Notice that if such a string contains only
characters above-Latin1 that match only themselves, that the node can be
downgraded to an EXACT-only node, which presents better optimization
possibilities, as we now have a fixed string known at compile time to be
required to be in the target string to match. Similarly if all
characters in the string match only other above-Latin1 characters
case-insensitively, the node can be downgraded to a regular EXACTFU node
(match, folding, using Unicode, not locale, rules). The code changes
for this could be done without accepting UTF-8 locales fully, but there
were edge cases which needed to be handled differently if I stopped
there, so I continued on.
In an EXACTFL node, all such characters are now folded at compile time
(just as before this commit), while the other characters whose folds are
locale-dependent are left unfolded. This means that they have to be
folded at execution time based on the locale in effect at the moment.
Again, this isn't a change from before. The difference is that now some
of the folds that need to be done at execution time (in regexec) are
potentially multi-char. Some of the code in regexec was trivial to
extend to account for this because of existing infrastructure, but the
part dealing with regex quantifiers, had to have more work.
Also the code that joins EXACTish nodes together had to be expanded to
account for the possibility of multi-character folds within locale
handling. This was fairly easy, because it already has infrastructure
to handle these under somewhat different circumstances.
3) In bracketed character classes, represented by ANYOF nodes, a new
inversion list was created giving the characters that should be matched
by this node when the runtime locale is UTF-8. The list is ignored
except under that circumstance. To do this, I created a new ANYOF type
which has an extra SV for the inversion list.
The edge case that caused the most difficulty is folding involving the
MICRO SIGN, U+00B5. It folds to the GREEK SMALL LETTER MU, as does the
GREEK CAPITAL LETTER MU. The MICRO SIGN is the only 0-255 range
character that folds to outside that range. The issue is that it
doesn't naturally fall out that it will match the CAP MU. If we let the
CAP MU fold to the samll mu at compile time (which it can because both
are above-Latin1 and so the fold is the same no matter what locale is in
effect), it could appear that the regnode can be downgraded away from
EXACTFL to EXACTFU, but doing so would cause the MICRO SIGN to not case
insensitvely match the CAP MU. This could be special cased in regcomp
and regexec, but I wanted to avoid that. Instead the mktables tables
are set up to include the CAP MU as a character whose presence forbids
the downgrading, so the special casing is in mktables, and not in the C
code.
Diffstat (limited to 'regcomp.h')
-rw-r--r-- | regcomp.h | 26 |
1 files changed, 23 insertions, 3 deletions
@@ -191,9 +191,23 @@ struct regnode_charclass_class { U32 classflags; /* and run-time */ }; -/* Synthetic start class; is a regnode_charclass_class plus an SV*. Note that - * the 'next_off' field is unused, as the SSC stands alone, so there is never a - * next node. */ +/* like above, but also has folds that are used only if the runtime locale is + * UTF-8. */ +struct regnode_charclass_posixl_fold { + U8 flags; /* ANYOF_POSIXL bit must go here */ + U8 type; + U16 next_off; + U32 arg1; /* used as ptr in S_regclass */ + char bitmap[ANYOF_BITMAP_SIZE]; /* both compile-time */ + U32 classflags; /* and run-time */ + SV* utf8_locale_list; /* list of code points matched by folds + in a UTF-8 locale */ +}; + +/* A synthetic start class; is a regnode_charclass_posixl_fold, plus an extra + * SV*, used only during its construction and which is not used by regexec.c. + * Note that the 'next_off' field is unused, as the SSC stands alone, so there + * is never a next node. */ struct regnode_ssc { U8 flags; /* ANYOF_POSIXL bit must go here */ U8 type; @@ -201,6 +215,8 @@ struct regnode_ssc { U32 arg1; /* used as ptr in S_regclass */ char bitmap[ANYOF_BITMAP_SIZE]; /* both compile-time */ U32 classflags; /* and run-time */ + SV* utf8_locale_list; /* list of code points matched by folds + in a UTF-8 locale */ SV* invlist; /* list of code points matched */ }; @@ -470,6 +486,7 @@ struct regnode_ssc { #define ANYOF_SIZE (sizeof(struct regnode_charclass)) #define ANYOF_POSIXL_SIZE (sizeof(regnode_charclass_posixl)) #define ANYOF_CLASS_SIZE ANYOF_POSIXL_SIZE +#define ANYOF_POSIXL_FOLD_SIZE (sizeof(regnode_charclass_posixl_fold)) #define ANYOF_FLAGS(p) ((p)->flags) @@ -522,8 +539,11 @@ struct regnode_ssc { #define ANYOF_SKIP ((ANYOF_SIZE - 1)/sizeof(regnode)) #define ANYOF_POSIXL_SKIP ((ANYOF_POSIXL_SIZE - 1)/sizeof(regnode)) +#define ANYOF_POSIXL_FOLD_SKIP ((ANYOF_POSIXL_FOLD_SIZE - 1)/sizeof(regnode)) #define ANYOF_CLASS_SKIP ANYOF_POSIXL_SKIP +#define ANYOF_UTF8_LOCALE_INVLIST(node) (((regnode_charclass_posixl_fold*) (node))->utf8_locale_list) + /* * Utility definitions. */ |