This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Prepare for Unicode 9.0
authorKarl Williamson <khw@cpan.org>
Thu, 16 Jun 2016 17:59:24 +0000 (11:59 -0600)
committerKarl Williamson <khw@cpan.org>
Wed, 22 Jun 2016 00:10:38 +0000 (18:10 -0600)
The major code changes needed to support Unicode 9.0 are to changes in
the boundary (break) rules, for things like \b{lb}, \b{wb}.
regen/mk_invlists.pl creates two-dimensional arrays for all these
properties.  To see if a given point in the target string is a break or
not, regexec.c looks up the entry in the property's table whose row
corresponds to the code point before the potential break, and whose
column corresponds to the one after.  Mostly this is completely
determining, but for some cases, extra context is required, and the
array entry indicates this, and there has to be specially crafted code
in regexec.c to handle each such possibility.  When a new release comes
along, mk_invlists.pl has to be changed to handle any new or changed
rules, and regexec.c has to be changed to handle any changes to the
custom code.

Unfortunately this is not a mature area of the Standard, and changes are
fairly common in new releases.  In part, this is because new types of
code points come along, which need new rules.  Sometimes it is because
they realized the previous version didn't work as well as it could.  An
example of the latter is that Unicode now realizes that Regional
Indicator (RI) characters come in pairs, and that one should be able to
break between each pair, but not within a pair.  Previous versions
treated any run of them as unbreakable.  (Regional Indicators are a
fairly recent type that was added to the Standard in 6.0, and things are
still getting shaken out.)

The other main changes to these rules also involve a fairly new type of
character, emojis.  We can expect further changes to these in the next
Unicode releases.

\b{gcb} for the first time, now depends on context (in rarely
encountered cases, like RI's), so the function had to be changed from a
simple table look-up to be more like the functions handling the other
break properties.

Some years ago I revamped mktables in part to try to make it require as
few manual interventions as possible when upgrading to a new version of
Unicode.  For example, a new data file in a release requires telling
mktables about it, but as long as it follows the format of existing
recent files, nothing else need be done to get whatever properties it
describes to be included.

Some of changes to mktables involved guessing, from existing limited
data, what the underlying paradigm for that data was.  The problem with
that is there may not have been a paradigm, just something they did ad
hoc, which can change at will; or I didn't understand their unstated
thinking, and guessed wrong.

Besides the boundary rule changes, the only change that the existing
mktables couldn't cope with was the addition of the Tangut script, whose
character names include the code point, like CJK UNIFIED IDEOGRAPH-3400
has always done.  The paradigm for this wasn't clear, since CJK was the
only script that had this characteristic, and so I hard-coded it into
mktables.  The way Tangut is structured may show that there is a
paradigm emerging (but we only have two examples, and there may not be a
paradigm at all), and so I have guessed one, and changed mktables to
assume this guessed paradigm.  If other scripts like this come along,
and I have guessed correctly, mktables will cope with these
automatically without manual intervention.

charclass_invlists.h
embed.fnc
embed.h
lib/Unicode/UCD.pm
lib/charnames.t
lib/unicore/mktables
pod/perlrebackslash.pod
proto.h
regcharclass.h
regen/mk_invlists.pl
regexec.c

index cb545cb..c92abcf 100644 (file)
@@ -2205,23 +2205,28 @@ static const UV _Perl_GCB_invlist[] = { /* for ASCII/Latin1 */
 
 #if defined(PERL_IN_REGEXEC_C)
 
-#define GCB_ENUM_COUNT 14
+#define GCB_ENUM_COUNT 19
 
 typedef enum {
        GCB_Other = 0,
        GCB_CR = 1,
        GCB_Control = 2,
-       GCB_Extend = 3,
-       GCB_L = 4,
-       GCB_LF = 5,
-       GCB_LV = 6,
-       GCB_LVT = 7,
-       GCB_Prepend = 8,
-       GCB_Regional_Indicator = 9,
-       GCB_SpacingMark = 10,
-       GCB_T = 11,
-       GCB_V = 12,
-       GCB_EDGE = 13
+       GCB_E_Base = 3,
+       GCB_E_Base_GAZ = 4,
+       GCB_E_Modifier = 5,
+       GCB_Extend = 6,
+       GCB_Glue_After_Zwj = 7,
+       GCB_L = 8,
+       GCB_LF = 9,
+       GCB_LV = 10,
+       GCB_LVT = 11,
+       GCB_Prepend = 12,
+       GCB_Regional_Indicator = 13,
+       GCB_SpacingMark = 14,
+       GCB_T = 15,
+       GCB_V = 16,
+       GCB_ZWJ = 17,
+       GCB_EDGE = 18
 } GCB_enum;
 
 static const GCB_enum _Perl_GCB_invmap[] = { /* for ASCII/Latin1 */
@@ -8364,7 +8369,7 @@ static const UV _Perl_LB_invlist[] = { /* for ASCII/Latin1 */
 
 #if defined(PERL_IN_REGEXEC_C)
 
-#define LB_ENUM_COUNT 36
+#define LB_ENUM_COUNT 39
 
 typedef enum {
        LB_Alphabetic = 0,
@@ -8377,32 +8382,35 @@ typedef enum {
        LB_Close_Punctuation = 7,
        LB_Combining_Mark = 8,
        LB_Contingent_Break = 9,
-       LB_Exclamation = 10,
-       LB_Glue = 11,
-       LB_H2 = 12,
-       LB_H3 = 13,
-       LB_Hebrew_Letter = 14,
-       LB_Hyphen = 15,
-       LB_Ideographic = 16,
-       LB_Infix_Numeric = 17,
-       LB_Inseparable = 18,
-       LB_JL = 19,
-       LB_JT = 20,
-       LB_JV = 21,
-       LB_Line_Feed = 22,
-       LB_Mandatory_Break = 23,
-       LB_Next_Line = 24,
-       LB_Nonstarter = 25,
-       LB_Numeric = 26,
-       LB_Open_Punctuation = 27,
-       LB_Postfix_Numeric = 28,
-       LB_Prefix_Numeric = 29,
-       LB_Quotation = 30,
-       LB_Regional_Indicator = 31,
-       LB_Space = 32,
-       LB_Word_Joiner = 33,
-       LB_ZWSpace = 34,
-       LB_EDGE = 35
+       LB_E_Base = 10,
+       LB_E_Modifier = 11,
+       LB_Exclamation = 12,
+       LB_Glue = 13,
+       LB_H2 = 14,
+       LB_H3 = 15,
+       LB_Hebrew_Letter = 16,
+       LB_Hyphen = 17,
+       LB_Ideographic = 18,
+       LB_Infix_Numeric = 19,
+       LB_Inseparable = 20,
+       LB_JL = 21,
+       LB_JT = 22,
+       LB_JV = 23,
+       LB_Line_Feed = 24,
+       LB_Mandatory_Break = 25,
+       LB_Next_Line = 26,
+       LB_Nonstarter = 27,
+       LB_Numeric = 28,
+       LB_Open_Punctuation = 29,
+       LB_Postfix_Numeric = 30,
+       LB_Prefix_Numeric = 31,
+       LB_Quotation = 32,
+       LB_Regional_Indicator = 33,
+       LB_Space = 34,
+       LB_Word_Joiner = 35,
+       LB_ZWJ = 36,
+       LB_ZWSpace = 37,
+       LB_EDGE = 38
 } LB_enum;
 
 static const LB_enum _Perl_LB_invmap[] = { /* for ASCII/Latin1 */
@@ -17945,29 +17953,34 @@ static const UV _Perl_WB_invlist[] = { /* for ASCII/Latin1 */
 
 #if defined(PERL_IN_REGEXEC_C)
 
-#define WB_ENUM_COUNT 20
+#define WB_ENUM_COUNT 25
 
 typedef enum {
        WB_Other = 0,
        WB_ALetter = 1,
        WB_CR = 2,
        WB_Double_Quote = 3,
-       WB_Extend = 4,
-       WB_ExtendNumLet = 5,
-       WB_Format = 6,
-       WB_Hebrew_Letter = 7,
-       WB_Katakana = 8,
-       WB_LF = 9,
-       WB_MidLetter = 10,
-       WB_MidNum = 11,
-       WB_MidNumLet = 12,
-       WB_Newline = 13,
-       WB_Numeric = 14,
-       WB_Perl_Tailored_HSpace = 15,
-       WB_Regional_Indicator = 16,
-       WB_Single_Quote = 17,
-       WB_EDGE = 18,
-       WB_UNKNOWN = 19
+       WB_E_Base = 4,
+       WB_E_Base_GAZ = 5,
+       WB_E_Modifier = 6,
+       WB_Extend = 7,
+       WB_ExtendNumLet = 8,
+       WB_Format = 9,
+       WB_Glue_After_Zwj = 10,
+       WB_Hebrew_Letter = 11,
+       WB_Katakana = 12,
+       WB_LF = 13,
+       WB_MidLetter = 14,
+       WB_MidNum = 15,
+       WB_MidNumLet = 16,
+       WB_Newline = 17,
+       WB_Numeric = 18,
+       WB_Perl_Tailored_HSpace = 19,
+       WB_Regional_Indicator = 20,
+       WB_Single_Quote = 21,
+       WB_ZWJ = 22,
+       WB_EDGE = 23,
+       WB_UNKNOWN = 24
 } WB_enum;
 
 static const WB_enum _Perl_WB_invmap[] = { /* for ASCII/Latin1 */
@@ -31270,23 +31283,28 @@ static const UV _Perl_GCB_invlist[] = { /* for EBCDIC 1047 */
 
 #if defined(PERL_IN_REGEXEC_C)
 
-#define GCB_ENUM_COUNT 14
+#define GCB_ENUM_COUNT 19
 
 typedef enum {
        GCB_Other = 0,
        GCB_CR = 1,
        GCB_Control = 2,
-       GCB_Extend = 3,
-       GCB_L = 4,
-       GCB_LF = 5,
-       GCB_LV = 6,
-       GCB_LVT = 7,
-       GCB_Prepend = 8,
-       GCB_Regional_Indicator = 9,
-       GCB_SpacingMark = 10,
-       GCB_T = 11,
-       GCB_V = 12,
-       GCB_EDGE = 13
+       GCB_E_Base = 3,
+       GCB_E_Base_GAZ = 4,
+       GCB_E_Modifier = 5,
+       GCB_Extend = 6,
+       GCB_Glue_After_Zwj = 7,
+       GCB_L = 8,
+       GCB_LF = 9,
+       GCB_LV = 10,
+       GCB_LVT = 11,
+       GCB_Prepend = 12,
+       GCB_Regional_Indicator = 13,
+       GCB_SpacingMark = 14,
+       GCB_T = 15,
+       GCB_V = 16,
+       GCB_ZWJ = 17,
+       GCB_EDGE = 18
 } GCB_enum;
 
 static const GCB_enum _Perl_GCB_invmap[] = { /* for EBCDIC 1047 */
@@ -37491,7 +37509,7 @@ static const UV _Perl_LB_invlist[] = { /* for EBCDIC 1047 */
 
 #if defined(PERL_IN_REGEXEC_C)
 
-#define LB_ENUM_COUNT 36
+#define LB_ENUM_COUNT 39
 
 typedef enum {
        LB_Alphabetic = 0,
@@ -37504,32 +37522,35 @@ typedef enum {
        LB_Close_Punctuation = 7,
        LB_Combining_Mark = 8,
        LB_Contingent_Break = 9,
-       LB_Exclamation = 10,
-       LB_Glue = 11,
-       LB_H2 = 12,
-       LB_H3 = 13,
-       LB_Hebrew_Letter = 14,
-       LB_Hyphen = 15,
-       LB_Ideographic = 16,
-       LB_Infix_Numeric = 17,
-       LB_Inseparable = 18,
-       LB_JL = 19,
-       LB_JT = 20,
-       LB_JV = 21,
-       LB_Line_Feed = 22,
-       LB_Mandatory_Break = 23,
-       LB_Next_Line = 24,
-       LB_Nonstarter = 25,
-       LB_Numeric = 26,
-       LB_Open_Punctuation = 27,
-       LB_Postfix_Numeric = 28,
-       LB_Prefix_Numeric = 29,
-       LB_Quotation = 30,
-       LB_Regional_Indicator = 31,
-       LB_Space = 32,
-       LB_Word_Joiner = 33,
-       LB_ZWSpace = 34,
-       LB_EDGE = 35
+       LB_E_Base = 10,
+       LB_E_Modifier = 11,
+       LB_Exclamation = 12,
+       LB_Glue = 13,
+       LB_H2 = 14,
+       LB_H3 = 15,
+       LB_Hebrew_Letter = 16,
+       LB_Hyphen = 17,
+       LB_Ideographic = 18,
+       LB_Infix_Numeric = 19,
+       LB_Inseparable = 20,
+       LB_JL = 21,
+       LB_JT = 22,
+       LB_JV = 23,
+       LB_Line_Feed = 24,
+       LB_Mandatory_Break = 25,
+       LB_Next_Line = 26,
+       LB_Nonstarter = 27,
+       LB_Numeric = 28,
+       LB_Open_Punctuation = 29,
+       LB_Postfix_Numeric = 30,
+       LB_Prefix_Numeric = 31,
+       LB_Quotation = 32,
+       LB_Regional_Indicator = 33,
+       LB_Space = 34,
+       LB_Word_Joiner = 35,
+       LB_ZWJ = 36,
+       LB_ZWSpace = 37,
+       LB_EDGE = 38
 } LB_enum;
 
 static const LB_enum _Perl_LB_invmap[] = { /* for EBCDIC 1047 */
@@ -47155,29 +47176,34 @@ static const UV _Perl_WB_invlist[] = { /* for EBCDIC 1047 */
 
 #if defined(PERL_IN_REGEXEC_C)
 
-#define WB_ENUM_COUNT 20
+#define WB_ENUM_COUNT 25
 
 typedef enum {
        WB_Other = 0,
        WB_ALetter = 1,
        WB_CR = 2,
        WB_Double_Quote = 3,
-       WB_Extend = 4,
-       WB_ExtendNumLet = 5,
-       WB_Format = 6,
-       WB_Hebrew_Letter = 7,
-       WB_Katakana = 8,
-       WB_LF = 9,
-       WB_MidLetter = 10,
-       WB_MidNum = 11,
-       WB_MidNumLet = 12,
-       WB_Newline = 13,
-       WB_Numeric = 14,
-       WB_Perl_Tailored_HSpace = 15,
-       WB_Regional_Indicator = 16,
-       WB_Single_Quote = 17,
-       WB_EDGE = 18,
-       WB_UNKNOWN = 19
+       WB_E_Base = 4,
+       WB_E_Base_GAZ = 5,
+       WB_E_Modifier = 6,
+       WB_Extend = 7,
+       WB_ExtendNumLet = 8,
+       WB_Format = 9,
+       WB_Glue_After_Zwj = 10,
+       WB_Hebrew_Letter = 11,
+       WB_Katakana = 12,
+       WB_LF = 13,
+       WB_MidLetter = 14,
+       WB_MidNum = 15,
+       WB_MidNumLet = 16,
+       WB_Newline = 17,
+       WB_Numeric = 18,
+       WB_Perl_Tailored_HSpace = 19,
+       WB_Regional_Indicator = 20,
+       WB_Single_Quote = 21,
+       WB_ZWJ = 22,
+       WB_EDGE = 23,
+       WB_UNKNOWN = 24
 } WB_enum;
 
 static const WB_enum _Perl_WB_invmap[] = { /* for EBCDIC 1047 */
@@ -60661,23 +60687,28 @@ static const UV _Perl_GCB_invlist[] = { /* for EBCDIC 037 */
 
 #if defined(PERL_IN_REGEXEC_C)
 
-#define GCB_ENUM_COUNT 14
+#define GCB_ENUM_COUNT 19
 
 typedef enum {
        GCB_Other = 0,
        GCB_CR = 1,
        GCB_Control = 2,
-       GCB_Extend = 3,
-       GCB_L = 4,
-       GCB_LF = 5,
-       GCB_LV = 6,
-       GCB_LVT = 7,
-       GCB_Prepend = 8,
-       GCB_Regional_Indicator = 9,
-       GCB_SpacingMark = 10,
-       GCB_T = 11,
-       GCB_V = 12,
-       GCB_EDGE = 13
+       GCB_E_Base = 3,
+       GCB_E_Base_GAZ = 4,
+       GCB_E_Modifier = 5,
+       GCB_Extend = 6,
+       GCB_Glue_After_Zwj = 7,
+       GCB_L = 8,
+       GCB_LF = 9,
+       GCB_LV = 10,
+       GCB_LVT = 11,
+       GCB_Prepend = 12,
+       GCB_Regional_Indicator = 13,
+       GCB_SpacingMark = 14,
+       GCB_T = 15,
+       GCB_V = 16,
+       GCB_ZWJ = 17,
+       GCB_EDGE = 18
 } GCB_enum;
 
 static const GCB_enum _Perl_GCB_invmap[] = { /* for EBCDIC 037 */
@@ -66874,7 +66905,7 @@ static const UV _Perl_LB_invlist[] = { /* for EBCDIC 037 */
 
 #if defined(PERL_IN_REGEXEC_C)
 
-#define LB_ENUM_COUNT 36
+#define LB_ENUM_COUNT 39
 
 typedef enum {
        LB_Alphabetic = 0,
@@ -66887,32 +66918,35 @@ typedef enum {
        LB_Close_Punctuation = 7,
        LB_Combining_Mark = 8,
        LB_Contingent_Break = 9,
-       LB_Exclamation = 10,
-       LB_Glue = 11,
-       LB_H2 = 12,
-       LB_H3 = 13,
-       LB_Hebrew_Letter = 14,
-       LB_Hyphen = 15,
-       LB_Ideographic = 16,
-       LB_Infix_Numeric = 17,
-       LB_Inseparable = 18,
-       LB_JL = 19,
-       LB_JT = 20,
-       LB_JV = 21,
-       LB_Line_Feed = 22,
-       LB_Mandatory_Break = 23,
-       LB_Next_Line = 24,
-       LB_Nonstarter = 25,
-       LB_Numeric = 26,
-       LB_Open_Punctuation = 27,
-       LB_Postfix_Numeric = 28,
-       LB_Prefix_Numeric = 29,
-       LB_Quotation = 30,
-       LB_Regional_Indicator = 31,
-       LB_Space = 32,
-       LB_Word_Joiner = 33,
-       LB_ZWSpace = 34,
-       LB_EDGE = 35
+       LB_E_Base = 10,
+       LB_E_Modifier = 11,
+       LB_Exclamation = 12,
+       LB_Glue = 13,
+       LB_H2 = 14,
+       LB_H3 = 15,
+       LB_Hebrew_Letter = 16,
+       LB_Hyphen = 17,
+       LB_Ideographic = 18,
+       LB_Infix_Numeric = 19,
+       LB_Inseparable = 20,
+       LB_JL = 21,
+       LB_JT = 22,
+       LB_JV = 23,
+       LB_Line_Feed = 24,
+       LB_Mandatory_Break = 25,
+       LB_Next_Line = 26,
+       LB_Nonstarter = 27,
+       LB_Numeric = 28,
+       LB_Open_Punctuation = 29,
+       LB_Postfix_Numeric = 30,
+       LB_Prefix_Numeric = 31,
+       LB_Quotation = 32,
+       LB_Regional_Indicator = 33,
+       LB_Space = 34,
+       LB_Word_Joiner = 35,
+       LB_ZWJ = 36,
+       LB_ZWSpace = 37,
+       LB_EDGE = 38
 } LB_enum;
 
 static const LB_enum _Perl_LB_invmap[] = { /* for EBCDIC 037 */
@@ -76526,29 +76560,34 @@ static const UV _Perl_WB_invlist[] = { /* for EBCDIC 037 */
 
 #if defined(PERL_IN_REGEXEC_C)
 
-#define WB_ENUM_COUNT 20
+#define WB_ENUM_COUNT 25
 
 typedef enum {
        WB_Other = 0,
        WB_ALetter = 1,
        WB_CR = 2,
        WB_Double_Quote = 3,
-       WB_Extend = 4,
-       WB_ExtendNumLet = 5,
-       WB_Format = 6,
-       WB_Hebrew_Letter = 7,
-       WB_Katakana = 8,
-       WB_LF = 9,
-       WB_MidLetter = 10,
-       WB_MidNum = 11,
-       WB_MidNumLet = 12,
-       WB_Newline = 13,
-       WB_Numeric = 14,
-       WB_Perl_Tailored_HSpace = 15,
-       WB_Regional_Indicator = 16,
-       WB_Single_Quote = 17,
-       WB_EDGE = 18,
-       WB_UNKNOWN = 19
+       WB_E_Base = 4,
+       WB_E_Base_GAZ = 5,
+       WB_E_Modifier = 6,
+       WB_Extend = 7,
+       WB_ExtendNumLet = 8,
+       WB_Format = 9,
+       WB_Glue_After_Zwj = 10,
+       WB_Hebrew_Letter = 11,
+       WB_Katakana = 12,
+       WB_LF = 13,
+       WB_MidLetter = 14,
+       WB_MidNum = 15,
+       WB_MidNumLet = 16,
+       WB_Newline = 17,
+       WB_Numeric = 18,
+       WB_Perl_Tailored_HSpace = 19,
+       WB_Regional_Indicator = 20,
+       WB_Single_Quote = 21,
+       WB_ZWJ = 22,
+       WB_EDGE = 23,
+       WB_UNKNOWN = 24
 } WB_enum;
 
 static const WB_enum _Perl_WB_invmap[] = { /* for EBCDIC 037 */
@@ -87735,116 +87774,142 @@ static const UV XPosixXDigit_invlist[] = { /* for EBCDIC 037 */
 
 #if defined(PERL_IN_REGEXEC_C)
 
-static const bool GCB_table[14][14] = {
-   /* 'edg' stands for 'EDGE' */
-/*        XX CR CN EX  L LF LV LVT PP RI SM  T  V edg */
-/* XX */ { 1, 1, 1, 0, 1, 1, 1,  1, 1, 1, 0, 1, 1,  1 },
-/* CR */ { 1, 1, 1, 1, 1, 0, 1,  1, 1, 1, 1, 1, 1,  1 },
-/* CN */ { 1, 1, 1, 1, 1, 1, 1,  1, 1, 1, 1, 1, 1,  1 },
-/* EX */ { 1, 1, 1, 0, 1, 1, 1,  1, 1, 1, 0, 1, 1,  1 },
-/* L  */ { 1, 1, 1, 0, 0, 1, 0,  0, 1, 1, 0, 1, 0,  1 },
-/* LF */ { 1, 1, 1, 1, 1, 1, 1,  1, 1, 1, 1, 1, 1,  1 },
-/* LV */ { 1, 1, 1, 0, 1, 1, 1,  1, 1, 1, 0, 0, 0,  1 },
-/* LVT*/ { 1, 1, 1, 0, 1, 1, 1,  1, 1, 1, 0, 0, 1,  1 },
-/* PP */ { 0, 1, 1, 0, 0, 1, 0,  0, 0, 0, 0, 0, 0,  1 },
-/* RI */ { 1, 1, 1, 0, 1, 1, 1,  1, 1, 0, 0, 1, 1,  1 },
-/* SM */ { 1, 1, 1, 0, 1, 1, 1,  1, 1, 1, 0, 1, 1,  1 },
-/* T  */ { 1, 1, 1, 0, 1, 1, 1,  1, 1, 1, 0, 0, 1,  1 },
-/* V  */ { 1, 1, 1, 0, 1, 1, 1,  1, 1, 1, 0, 0, 0,  1 },
-/* edg*/ { 1, 1, 1, 1, 1, 1, 1,  1, 1, 1, 1, 1, 1,  0 }
+#define GCB_NOBREAK      0
+#define GCB_BREAKABLE    1
+#define GCB_RI_then_RI   2
+#define GCB_EX_then_EM   3
+
+static const U8 GCB_table[19][19] = {
+   /* 'edg' stands for 'EDGE'; other lowercase names are placeholders
+    * for property values not defined until a later Unicode release, so
+    * are irrelevant in this one, as they are not assigned to any code
+    * points */
+/*        XX CR CN  a  b  c EX  d  L LF LV LVT PP RI SM  T  V  e edg */
+/* XX */ { 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1,  1, 1, 1, 0, 1, 1, 0,  1 },
+/* CR */ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1,  1, 1, 1, 1, 1, 1, 1,  1 },
+/* CN */ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  1, 1, 1, 1, 1, 1, 1,  1 },
+/* a  */ { 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1,  1, 1, 1, 0, 1, 1, 0,  1 },
+/* b  */ { 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1,  1, 1, 1, 0, 1, 1, 0,  1 },
+/* c  */ { 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1,  1, 1, 1, 0, 1, 1, 0,  1 },
+/* EX */ { 1, 1, 1, 1, 1, 3, 0, 1, 1, 1, 1,  1, 1, 1, 0, 1, 1, 0,  1 },
+/* d  */ { 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1,  1, 1, 1, 0, 1, 1, 0,  1 },
+/* L  */ { 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0,  0, 1, 1, 0, 1, 0, 0,  1 },
+/* LF */ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  1, 1, 1, 1, 1, 1, 1,  1 },
+/* LV */ { 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1,  1, 1, 1, 0, 0, 0, 0,  1 },
+/* LVT*/ { 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1,  1, 1, 1, 0, 0, 1, 0,  1 },
+/* PP */ { 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0,  0, 0, 0, 0, 0, 0, 0,  1 },
+/* RI */ { 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1,  1, 1, 2, 0, 1, 1, 0,  1 },
+/* SM */ { 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1,  1, 1, 1, 0, 1, 1, 0,  1 },
+/* T  */ { 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1,  1, 1, 1, 0, 0, 1, 0,  1 },
+/* V  */ { 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1,  1, 1, 1, 0, 0, 0, 0,  1 },
+/* e  */ { 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1,  1, 1, 1, 0, 1, 1, 0,  1 },
+/* edg*/ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  1, 1, 1, 1, 1, 1, 1,  0 }
 };
 
 #define LB_NOBREAK                        0
 #define LB_BREAKABLE                      1
 #define LB_NOBREAK_EVEN_WITH_SP_BETWEEN   2
-#define LB_CM_foo                         3
+#define LB_CM_ZWJ_foo                     3
 #define LB_SP_foo                         6
 #define LB_PR_or_PO_then_OP_or_HY         9
 #define LB_SY_or_IS_then_various         11
 #define LB_HY_or_BA_then_foo             13
-#define LB_various_then_PO_or_PR         16
+#define LB_RI_then_RI                    15
+#define LB_various_then_PO_or_PR         32
 
-static const U8 LB_table[36][36] = {
-   /* 'edg' stands for 'EDGE' */
-/*        AL BA BB B2 SY CR CP CL CM CB EX GL H2 H3 HL HY ID IS IN JL JT JV LF BK NL NS NU OP PO PR QU RI SP WJ ZW edg */
-/* AL */ { 0, 0, 1, 1, 2, 0, 2, 2, 0, 1, 2, 0, 1, 1, 0, 0, 1, 2, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0,  1 },
-/* BA */ {14, 0,14,14, 2, 0, 2, 2, 0, 1, 2,14,14,14,14, 0,14, 2,14,14,14,14, 0, 0, 0, 0,14,14,14,14, 0,14, 0, 0, 0,  1 },
-/* BB */ { 0, 0, 0, 0, 2, 0, 2, 2, 0, 1, 2, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  1 },
-/* B2 */ { 1, 0, 1, 2, 2, 0, 2, 2, 0, 1, 2, 0, 1, 1, 1, 0, 1, 2, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0,  1 },
-/* SY */ { 1, 0, 1, 1, 2, 0, 2, 2, 0, 1, 2, 0, 1, 1, 0, 0, 1, 2, 1, 1, 1, 1, 0, 0, 0, 0,12, 1,17,17, 0, 1, 0, 0, 0,  1 },
-/* CR */ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  1 },
-/* CP */ { 0, 0, 1, 1, 2, 0, 2, 2, 0, 1, 2, 0, 1, 1, 0, 0, 1, 2, 1, 1, 1, 1, 0, 0, 0, 2, 0, 1,17,17, 0, 1, 0, 0, 0,  1 },
-/* CL */ { 1, 0, 1, 1, 2, 0, 2, 2, 0, 1, 2, 0, 1, 1, 1, 0, 1, 2, 1, 1, 1, 1, 0, 0, 0, 2, 1, 1,17,17, 0, 1, 0, 0, 0,  1 },
-/* CM */ { 3, 3, 3, 3, 3, 0, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 0, 3, 0,  1 },
-/* CB */ { 1, 1, 1, 1, 2, 0, 2, 2, 0, 1, 2, 0, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0,  1 },
-/* EX */ { 1, 0, 1, 1, 2, 0, 2, 2, 0, 1, 2, 0, 1, 1, 1, 0, 1, 2, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0,  1 },
-/* GL */ { 0, 0, 0, 0, 2, 0, 2, 2, 0, 0, 2, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  1 },
-/* H2 */ { 1, 0, 1, 1, 2, 0, 2, 2, 0, 1, 2, 0, 1, 1, 1, 0, 1, 2, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0,  1 },
-/* H3 */ { 1, 0, 1, 1, 2, 0, 2, 2, 0, 1, 2, 0, 1, 1, 1, 0, 1, 2, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0,  1 },
-/* HL */ { 0, 0, 1, 1, 2, 0, 2, 2, 0, 1, 2, 0, 1, 1, 0, 0, 1, 2, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0,  1 },
-/* HY */ {14, 0,14,14, 2, 0, 2, 2, 0, 1, 2,14,14,14,14, 0,14, 2,14,14,14,14, 0, 0, 0, 0,13,14,14,14, 0,14, 0, 0, 0,  1 },
-/* ID */ { 1, 0, 1, 1, 2, 0, 2, 2, 0, 1, 2, 0, 1, 1, 1, 0, 1, 2, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0,  1 },
-/* IS */ { 0, 0, 1, 1, 2, 0, 2, 2, 0, 1, 2, 0, 1, 1, 0, 0, 1, 2, 1, 1, 1, 1, 0, 0, 0, 0,12, 1,17,17, 0, 1, 0, 0, 0,  1 },
-/* IN */ { 1, 0, 1, 1, 2, 0, 2, 2, 0, 1, 2, 0, 1, 1, 1, 0, 1, 2, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0,  1 },
-/* JL */ { 1, 0, 1, 1, 2, 0, 2, 2, 0, 1, 2, 0, 0, 0, 1, 0, 1, 2, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0,  1 },
-/* JT */ { 1, 0, 1, 1, 2, 0, 2, 2, 0, 1, 2, 0, 1, 1, 1, 0, 1, 2, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0,  1 },
-/* JV */ { 1, 0, 1, 1, 2, 0, 2, 2, 0, 1, 2, 0, 1, 1, 1, 0, 1, 2, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0,  1 },
-/* LF */ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  1 },
-/* BK */ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  1 },
-/* NL */ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  1 },
-/* NS */ { 1, 0, 1, 1, 2, 0, 2, 2, 0, 1, 2, 0, 1, 1, 1, 0, 1, 2, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0,  1 },
-/* NU */ { 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 2, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,  1 },
-/* OP */ { 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 0, 2, 0,  1 },
-/* PO */ { 0, 0, 1, 1, 2, 0, 2, 2, 0, 1, 2, 0, 1, 1, 0, 0, 1, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0,10, 1, 1, 0, 1, 0, 0, 0,  1 },
-/* PR */ { 0, 0, 1, 1, 2, 0, 2, 2, 0, 1, 2, 0, 0, 0, 0, 0, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0,10, 1, 1, 0, 1, 0, 0, 0,  1 },
-/* QU */ { 0, 0, 0, 0, 2, 0, 2, 2, 0, 0, 2, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0,  1 },
-/* RI */ { 1, 0, 1, 1, 2, 0, 2, 2, 0, 1, 2, 0, 1, 1, 1, 0, 1, 2, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0,  1 },
-/* SP */ { 7, 7, 7, 7, 8, 0, 8, 8, 7, 7, 8, 7, 7, 7, 7, 7, 7, 8, 7, 7, 7, 7, 0, 0, 0, 7, 7, 7, 7, 7, 7, 7, 0, 8, 0,  1 },
-/* WJ */ { 0, 0, 0, 0, 2, 0, 2, 2, 0, 0, 2, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  1 },
-/* ZW */ { 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0,  1 },
-/* edg*/ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  0 }
+static const U8 LB_table[39][39] = {
+   /* 'edg' stands for 'EDGE'; other lowercase names are placeholders for property values not defined until a later Unicode
+    * release, so are irrelevant in this one, as they are not assigned to any code points */
+/*        AL BA BB B2 SY CR CP CL CM CB  a  b EX GL H2 H3 HL HY ID IS IN JL JT JV LF BK NL NS NU OP PO PR QU RI SP WJ  c ZW edg */
+/* AL */ { 0, 0, 1, 1, 2, 0, 2, 2, 0, 1, 1, 1, 2, 0, 1, 1, 0, 0, 1, 2, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,  1 },
+/* BA */ {14, 0,14,14, 2, 0, 2, 2, 0, 1,14,14, 2,14,14,14,14, 0,14, 2,14,14,14,14, 0, 0, 0, 0,14,14,14,14, 0,14, 0, 0, 0, 0,  1 },
+/* BB */ { 0, 0, 0, 0, 2, 0, 2, 2, 0, 1, 0, 0, 2, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  1 },
+/* B2 */ { 1, 0, 1, 2, 2, 0, 2, 2, 0, 1, 1, 1, 2, 0, 1, 1, 1, 0, 1, 2, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0,  1 },
+/* SY */ { 1, 0, 1, 1, 2, 0, 2, 2, 0, 1, 1, 1, 2, 0, 1, 1, 0, 0, 1, 2, 1, 1, 1, 1, 0, 0, 0, 0,12, 1,33,33, 0, 1, 0, 0, 0, 0,  1 },
+/* CR */ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  1 },
+/* CP */ { 0, 0, 1, 1, 2, 0, 2, 2, 0, 1, 1, 1, 2, 0, 1, 1, 0, 0, 1, 2, 1, 1, 1, 1, 0, 0, 0, 2, 0, 1,33,33, 0, 1, 0, 0, 0, 0,  1 },
+/* CL */ { 1, 0, 1, 1, 2, 0, 2, 2, 0, 1, 1, 1, 2, 0, 1, 1, 1, 0, 1, 2, 1, 1, 1, 1, 0, 0, 0, 2, 1, 1,33,33, 0, 1, 0, 0, 0, 0,  1 },
+/* CM */ { 3, 3, 3, 3, 3, 0, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 0,  1 },
+/* CB */ { 1, 1, 1, 1, 2, 0, 2, 2, 0, 1, 1, 1, 2, 0, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0,  1 },
+/* a  */ { 1, 0, 1, 1, 2, 0, 2, 2, 0, 1, 1, 0, 2, 0, 1, 1, 1, 0, 1, 2, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0,  1 },
+/* b  */ { 1, 0, 1, 1, 2, 0, 2, 2, 0, 1, 1, 1, 2, 0, 1, 1, 1, 0, 1, 2, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0,  1 },
+/* EX */ { 1, 0, 1, 1, 2, 0, 2, 2, 0, 1, 1, 1, 2, 0, 1, 1, 1, 0, 1, 2, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0,  1 },
+/* GL */ { 0, 0, 0, 0, 2, 0, 2, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  1 },
+/* H2 */ { 1, 0, 1, 1, 2, 0, 2, 2, 0, 1, 1, 1, 2, 0, 1, 1, 1, 0, 1, 2, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0,  1 },
+/* H3 */ { 1, 0, 1, 1, 2, 0, 2, 2, 0, 1, 1, 1, 2, 0, 1, 1, 1, 0, 1, 2, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0,  1 },
+/* HL */ { 0, 0, 1, 1, 2, 0, 2, 2, 0, 1, 1, 1, 2, 0, 1, 1, 0, 0, 1, 2, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,  1 },
+/* HY */ {14, 0,14,14, 2, 0, 2, 2, 0, 1,14,14, 2,14,14,14,14, 0,14, 2,14,14,14,14, 0, 0, 0, 0,13,14,14,14, 0,14, 0, 0, 0, 0,  1 },
+/* ID */ { 1, 0, 1, 1, 2, 0, 2, 2, 0, 1, 1, 1, 2, 0, 1, 1, 1, 0, 1, 2, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0,  1 },
+/* IS */ { 0, 0, 1, 1, 2, 0, 2, 2, 0, 1, 1, 1, 2, 0, 1, 1, 0, 0, 1, 2, 1, 1, 1, 1, 0, 0, 0, 0,12, 1,33,33, 0, 1, 0, 0, 0, 0,  1 },
+/* IN */ { 1, 0, 1, 1, 2, 0, 2, 2, 0, 1, 1, 1, 2, 0, 1, 1, 1, 0, 1, 2, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0,  1 },
+/* JL */ { 1, 0, 1, 1, 2, 0, 2, 2, 0, 1, 1, 1, 2, 0, 0, 0, 1, 0, 1, 2, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0,  1 },
+/* JT */ { 1, 0, 1, 1, 2, 0, 2, 2, 0, 1, 1, 1, 2, 0, 1, 1, 1, 0, 1, 2, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0,  1 },
+/* JV */ { 1, 0, 1, 1, 2, 0, 2, 2, 0, 1, 1, 1, 2, 0, 1, 1, 1, 0, 1, 2, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0,  1 },
+/* LF */ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  1 },
+/* BK */ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  1 },
+/* NL */ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  1 },
+/* NS */ { 1, 0, 1, 1, 2, 0, 2, 2, 0, 1, 1, 1, 2, 0, 1, 1, 1, 0, 1, 2, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0,  1 },
+/* NU */ { 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 2, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,  1 },
+/* OP */ { 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 0,  1 },
+/* PO */ { 0, 0, 1, 1, 2, 0, 2, 2, 0, 1, 1, 1, 2, 0, 1, 1, 0, 0, 1, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0,10, 1, 1, 0, 1, 0, 0, 0, 0,  1 },
+/* PR */ { 0, 0, 1, 1, 2, 0, 2, 2, 0, 1, 0, 0, 2, 0, 0, 0, 0, 0, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0,10, 1, 1, 0, 1, 0, 0, 0, 0,  1 },
+/* QU */ { 0, 0, 0, 0, 2, 0, 2, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0,  1 },
+/* RI */ { 1, 0, 1, 1, 2, 0, 2, 2, 0, 1, 1, 1, 2, 0, 1, 1, 1, 0, 1, 2, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0,15, 0, 0, 0, 0,  1 },
+/* SP */ { 7, 7, 7, 7, 8, 0, 8, 8, 7, 7, 7, 7, 8, 7, 7, 7, 7, 7, 7, 8, 7, 7, 7, 7, 0, 0, 0, 7, 7, 7, 7, 7, 7, 7, 0, 8, 7, 0,  1 },
+/* WJ */ { 0, 0, 0, 0, 2, 0, 2, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  1 },
+/* c  */ { 3, 3, 3, 3, 3, 0, 3, 3, 0, 3, 0, 0, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 0, 3, 0, 0,  1 },
+/* ZW */ { 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0,  1 },
+/* edg*/ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  0 }
 };
 
 #define WB_NOBREAK                        0
 #define WB_BREAKABLE                      1
 #define WB_hs_then_hs                     2
-#define WB_Ex_or_FO_then_foo              3
+#define WB_Ex_or_FO_or_ZWJ_then_foo       3
 #define WB_DQ_then_HL                     4
 #define WB_HL_then_DQ                     6
 #define WB_LE_or_HL_then_MB_or_ML_or_SQ   8
 #define WB_MB_or_ML_or_SQ_then_LE_or_HL  10
 #define WB_MB_or_MN_or_SQ_then_NU        12
 #define WB_NU_then_MB_or_MN_or_SQ        14
+#define WB_RI_then_RI                    16
 
-static const U8 WB_table[19][19] = {
-   /* 'Ext' stands for 'Extend'; 'edg' stands for 'EDGE'; 'hs' stands
-    * for 'Perl_Tailored_HSpace'; 'unk' stands for 'UNKNOWN' */
-/*        XX LE CR DQ Ext EX FO HL KA LF ML MN MB NL NU hs RI SQ edg */
-/* XX */ { 1, 1, 1, 1,  0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  1 },
-/* LE */ { 1, 0, 1, 1,  0, 0, 0, 0, 1, 1, 9, 1, 9, 1, 0, 1, 1, 9,  1 },
-/* CR */ { 1, 1, 0, 1,  1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1,  1 },
-/* DQ */ { 1, 1, 1, 1,  0, 1, 0, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  1 },
-/* Ext*/ { 3, 3, 1, 3,  0, 3, 0, 3, 3, 1, 3, 3, 3, 1, 3, 1, 3, 3,  1 },
-/* EX */ { 1, 0, 1, 1,  0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1,  1 },
-/* FO */ { 3, 3, 1, 3,  0, 3, 0, 3, 3, 1, 3, 3, 3, 1, 3, 1, 3, 3,  1 },
-/* HL */ { 1, 0, 1, 7,  0, 0, 0, 0, 1, 1, 9, 1, 9, 1, 0, 1, 1, 8,  1 },
-/* KA */ { 1, 1, 1, 1,  0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1,  1 },
-/* LF */ { 1, 1, 0, 1,  1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1,  1 },
-/* ML */ { 1,11, 1, 1,  0, 1, 0,11, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  1 },
-/* MN */ { 1, 1, 1, 1,  0, 1, 0, 1, 1, 1, 1, 1, 1, 1,13, 1, 1, 1,  1 },
-/* MB */ { 1,11, 1, 1,  0, 1, 0,11, 1, 1, 1, 1, 1, 1,13, 1, 1, 1,  1 },
-/* NL */ { 1, 1, 0, 1,  1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1,  1 },
-/* NU */ { 1, 0, 1, 1,  0, 0, 0, 0, 1, 1, 1,15,15, 1, 0, 1, 1,15,  1 },
-/* hs */ { 1, 1, 0, 1,  0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 2, 1, 1,  1 },
-/* RI */ { 1, 1, 1, 1,  0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1,  1 },
-/* SQ */ { 1,11, 1, 1,  0, 1, 0,11, 1, 1, 1, 1, 1, 1,13, 1, 1, 1,  1 },
-/* edg*/ { 1, 1, 1, 1,  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  0 }
+static const U8 WB_table[24][24] = {
+   /* 'Ext' stands for 'Extend'; 'edg' stands for 'EDGE'; 'hs' stands for
+    * 'Perl_Tailored_HSpace'; 'unk' stands for 'UNKNOWN'; other lowercase names are
+    * placeholders for property values not defined until a later Unicode release, so
+    * are irrelevant in this one, as they are not assigned to any code points */
+/*        XX LE CR DQ  a  b  c Ext EX FO  d HL KA LF ML MN MB NL NU hs RI SQ  e edg */
+/* XX */ { 1, 1, 1, 1, 1, 1, 1,  0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,  1 },
+/* LE */ { 1, 0, 1, 1, 1, 1, 1,  0, 0, 0, 1, 0, 1, 1, 9, 1, 9, 1, 0, 1, 1, 9, 0,  1 },
+/* CR */ { 1, 1, 0, 1, 1, 1, 1,  1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1,  1 },
+/* DQ */ { 1, 1, 1, 1, 1, 1, 1,  0, 1, 0, 1, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,  1 },
+/* a  */ { 1, 1, 1, 1, 1, 1, 0,  0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,  1 },
+/* b  */ { 1, 1, 1, 1, 1, 1, 0,  0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,  1 },
+/* c  */ { 1, 1, 1, 1, 1, 1, 1,  0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,  1 },
+/* Ext*/ { 3, 3, 1, 3, 3, 3, 3,  0, 3, 0, 3, 3, 3, 1, 3, 3, 3, 1, 3, 1, 3, 3, 0,  1 },
+/* EX */ { 1, 0, 1, 1, 1, 1, 1,  0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0,  1 },
+/* FO */ { 3, 3, 1, 3, 3, 3, 3,  0, 3, 0, 3, 3, 3, 1, 3, 3, 3, 1, 3, 1, 3, 3, 0,  1 },
+/* d  */ { 1, 1, 1, 1, 1, 1, 1,  0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,  1 },
+/* HL */ { 1, 0, 1, 7, 1, 1, 1,  0, 0, 0, 1, 0, 1, 1, 9, 1, 9, 1, 0, 1, 1, 8, 0,  1 },
+/* KA */ { 1, 1, 1, 1, 1, 1, 1,  0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,  1 },
+/* LF */ { 1, 1, 0, 1, 1, 1, 1,  1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1,  1 },
+/* ML */ { 1,11, 1, 1, 1, 1, 1,  0, 1, 0, 1,11, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,  1 },
+/* MN */ { 1, 1, 1, 1, 1, 1, 1,  0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1,13, 1, 1, 1, 0,  1 },
+/* MB */ { 1,11, 1, 1, 1, 1, 1,  0, 1, 0, 1,11, 1, 1, 1, 1, 1, 1,13, 1, 1, 1, 0,  1 },
+/* NL */ { 1, 1, 0, 1, 1, 1, 1,  1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1,  1 },
+/* NU */ { 1, 0, 1, 1, 1, 1, 1,  0, 0, 0, 1, 0, 1, 1, 1,15,15, 1, 0, 1, 1,15, 0,  1 },
+/* hs */ { 1, 1, 0, 1, 1, 1, 1,  0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 2, 1, 1, 0,  1 },
+/* RI */ { 1, 1, 1, 1, 1, 1, 1,  0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,16, 1, 0,  1 },
+/* SQ */ { 1,11, 1, 1, 1, 1, 1,  0, 1, 0, 1,11, 1, 1, 1, 1, 1, 1,13, 1, 1, 1, 0,  1 },
+/* e  */ { 3, 3, 1, 3, 3, 0, 3,  0, 3, 0, 0, 3, 3, 1, 3, 3, 3, 1, 3, 1, 3, 3, 0,  1 },
+/* edg*/ { 1, 1, 1, 1, 1, 1, 1,  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  0 }
 };
 
 #endif /* defined(PERL_IN_REGEXEC_C) */
 
 /* Generated from:
- * 66726fe32be96a422e8c9b45bc9daf61e068d988c99ff41112972ef721365521 lib/Unicode/UCD.pm
+ * de6076d81bc4e85f179377ded4c68f3b257c8f7990227d4302eca442fda558f8 lib/Unicode/UCD.pm
  * ae98bec7e4f0564758eed81eca5015481ba32581f8a735a825b71b3bba714450 lib/unicore/ArabicShaping.txt
  * 1687fe5994eb7e5c0dab8503fc2a1b3b479d91af9d3b8055941c9bd791f7d0b5 lib/unicore/BidiBrackets.txt
  * 350d1302116194b0b21def287434b55c5088098fbc726e879f7420a391965643 lib/unicore/BidiMirroring.txt
@@ -87887,8 +87952,8 @@ static const U8 WB_table[19][19] = {
  * 1a0687fb9c6c4567e853913549df0944fe40821279a3e9cdaa6ab8679bc286fd lib/unicore/extracted/DLineBreak.txt
  * 40bcfed3ca727c19e1331f6c33806231d5f7eeeabd2e6a9e06a3740c85d0c250 lib/unicore/extracted/DNumType.txt
  * a18d502bad39d527ac5586d7bc93e29f565859e3bcc24ada627eff606d6f5fed lib/unicore/extracted/DNumValues.txt
- * 45321b549a605b65ead1e83cdb90fdd9c5a6c8731a537197f335bab251b4e778 lib/unicore/mktables
+ * 4fbcc500e9215a31d39fa3fba793a4c893285e7d19912fc86fa6518120ecc4e1 lib/unicore/mktables
  * 462c9aaa608fb2014cd9649af1c5c009485c60b9c8b15b89401fdc10cf6161c6 lib/unicore/version
  * 913d2f93f3cb6cdf1664db888bf840bc4eb074eef824e082fceda24a9445e60c regen/charset_translations.pl
- * 12bd58cb9d5a99f631ca95e269f7f9c90dacaf81020efa5d95a995f3cdc19200 regen/mk_invlists.pl
+ * 11011bc761487f5a63c8135e67248394d4cdff6f8f204a41cdfbdc8131e79406 regen/mk_invlists.pl
  * ex: set ro: */
index f8c01e6..e4bfdee 100644 (file)
--- a/embed.fnc
+++ b/embed.fnc
@@ -2358,7 +2358,14 @@ Es       |void   |to_utf8_substr |NN regexp * prog
 Es     |bool   |to_byte_substr |NN regexp * prog
 ERsn   |I32    |reg_check_named_buff_matched   |NN const regexp *rex \
                                                |NN const regnode *scan
-EinR   |bool   |isGCB          |const GCB_enum before|const GCB_enum after
+EsR    |bool   |isGCB          |const GCB_enum before                  \
+                               |const GCB_enum after                   \
+                               |NN const U8 * const strbeg             \
+                               |NN const U8 * const curpos             \
+                               |const bool utf8_target
+EsR    |GCB_enum|backup_one_GCB|NN const U8 * const strbeg                     \
+                               |NN U8 ** curpos                                \
+                               |const bool utf8_target
 EsR    |bool   |isLB           |LB_enum before                         \
                                |LB_enum after                          \
                                |NN const U8 * const strbeg             \
diff --git a/embed.h b/embed.h
index f37b76b..f32f317 100644 (file)
--- a/embed.h
+++ b/embed.h
 #define advance_one_LB(a,b,c)  S_advance_one_LB(aTHX_ a,b,c)
 #define advance_one_SB(a,b,c)  S_advance_one_SB(aTHX_ a,b,c)
 #define advance_one_WB(a,b,c,d)        S_advance_one_WB(aTHX_ a,b,c,d)
+#define backup_one_GCB(a,b,c)  S_backup_one_GCB(aTHX_ a,b,c)
 #define backup_one_LB(a,b,c)   S_backup_one_LB(aTHX_ a,b,c)
 #define backup_one_SB(a,b,c)   S_backup_one_SB(aTHX_ a,b,c)
 #define backup_one_WB(a,b,c,d) S_backup_one_WB(aTHX_ a,b,c,d)
 #define find_byclass(a,b,c,d,e)        S_find_byclass(aTHX_ a,b,c,d,e)
 #define isFOO_lc(a,b)          S_isFOO_lc(aTHX_ a,b)
 #define isFOO_utf8_lc(a,b)     S_isFOO_utf8_lc(aTHX_ a,b)
-#define isGCB                  S_isGCB
+#define isGCB(a,b,c,d,e)       S_isGCB(aTHX_ a,b,c,d,e)
 #define isLB(a,b,c,d,e,f)      S_isLB(aTHX_ a,b,c,d,e,f)
 #define isSB(a,b,c,d,e,f)      S_isSB(aTHX_ a,b,c,d,e,f)
 #define isWB(a,b,c,d,e,f,g)    S_isWB(aTHX_ a,b,c,d,e,f,g)
index f48e4ca..276e9f5 100644 (file)
@@ -5,7 +5,7 @@ use warnings;
 no warnings 'surrogate';    # surrogates can be inputs to this
 use charnames ();
 
-our $VERSION = '0.65';
+our $VERSION = '0.66';
 
 require Exporter;
 
@@ -98,6 +98,9 @@ Unicode::UCD - Unicode character database
     use Unicode::UCD 'search_invlist';
     my $index = search_invlist(\@invlist, $code_point);
 
+    # The following function should be used only internally in
+    # implementations of the Unicode Normalization Algorithm, and there
+    # are better choices than it.
     use Unicode::UCD 'compexcl';
     my $compexcl = compexcl($codepoint);
 
@@ -1200,6 +1203,12 @@ sub bidi_types {
 
 =head2 B<compexcl()>
 
+WARNING: Unicode discourages the use of this function or any of the
+alternative mechanisms listed in this section (the documention of
+C<compexcl()>), except internally in implementations of the Unicode
+Normalization Algorithm.  You should be using L<Unicode::Normalize> directly
+instead of these.  Using these will likely lead to half-baked results.
+
     use Unicode::UCD 'compexcl';
 
     my $compexcl = compexcl(0x09dc);
@@ -3044,6 +3053,8 @@ L<Unicode::Normalize::NFD()|Unicode::Normalize>.
 
 Note that the mapping is the one that is specified in the Unicode data files,
 and to get the final decomposition, it may need to be applied recursively.
+Unicode in fact discourages use of this property except internally in
+implementations of the Unicode Normalization Algorithm.
 
 The fourth (index [3]) element (C<$default>) in the list returned for this
 format is 0.
index cd87350..9a5400c 100644 (file)
@@ -1009,7 +1009,7 @@ is("\N{U+1D0C5}", "\N{BYZANTINE MUSICAL SYMBOL FTHORA SKLIRON CHROMA VASIS}", 'V
         die "Can't open ../../lib/unicore/UnicodeData.txt: $!";
     while (<$fh>) {
         chomp;
-        my ($code, $name, undef, undef, undef, undef, undef, undef, undef, undef, $u1name) = split ";";
+        my ($code, $name, $category, undef, undef, undef, undef, undef, undef, undef, $u1name) = split ";";
         my $decimal = utf8::unicode_to_native(hex $code);
         $code = sprintf("%04X", $decimal) unless $::IS_ASCII;
 
@@ -1042,12 +1042,26 @@ is("\N{U+1D0C5}", "\N{BYZANTINE MUSICAL SYMBOL FTHORA SKLIRON CHROMA VASIS}", 'V
             /^(.*?);/;
             my $end_decimal = hex $1;
 
-            # Only the CJK (and the Hangul which are instead dealt with below)
-            # ones have names, and they all have the code point as part of the
-            # name, which we can construct
-            if ($name =~ /^<CJK/) {
+            # Only the ones whose category is a letter currently have names,
+            # and of those the Hangul Syllables are dealt with below
+            if ( $category eq 'Lo' && $name !~ /^Hangul/i) {
+
+                # The CJK ones all get translated to a particular form; we
+                # just capitalize any others in the hopes that Unicode will
+                # use the correct term in any future ones it might add.
+                if ($name =~ /^<CJK/) {
+                    $name = "CJK UNIFIED IDEOGRAPH";
+                }
+                else {
+                    $name =~ s/<//;
+                    $name =~ s/,.*//;
+                    $name = uc($name);
+                }
+
+                # They all have the code point as part of the name, which we
+                # can construct
                 for my $i ($decimal .. $end_decimal) {
-                    $names[$i] = sprintf "CJK UNIFIED IDEOGRAPH-%04X", $i;
+                    $names[$i] = sprintf "$name-%04X", $i;
                     my $block = $i >> $block_size_bits;
                     $algorithmic_names_count[$block]++;
                 }
index 7b25ba7..e8a2831 100644 (file)
@@ -45,7 +45,7 @@ sub NON_ASCII_PLATFORM { ord("A") != 65 }
 # expected, a warning will be generated.  If an older version is being
 # compiled, any bounds tests that fail in the generated test file (-maketest
 # option) will be marked as TODO.
-my $version_of_mk_invlist_bounds = v8.0.0;
+my $version_of_mk_invlist_bounds = v9.0.0;
 
 ##########################################################################
 #
@@ -11741,7 +11741,16 @@ END
                                           . $CMD_DELIM
                                           . $fields[$CHARNAME];
             }
-            elsif ($fields[$CHARNAME] =~ /^CJK/) {
+            elsif ($fields[$CATEGORY] eq 'Lo') {    # Is a letter
+
+                # All the CJK ranges like this have the name given as a
+                # special case in the next code line.  And for the others, we
+                # hope that Unicode continues to use the correct name in
+                # future releases, so we don't have to make further special
+                # cases.
+                my $name = ($fields[$CHARNAME] =~ /^CJK/)
+                           ? 'CJK UNIFIED IDEOGRAPH'
+                           : uc $fields[$CHARNAME];
 
                 # The name for these contains the code point itself, and all
                 # are defined to have the same base name, regardless of what
@@ -11753,7 +11762,7 @@ END
                                            . '='
                                            . $CP_IN_NAME
                                            . $CMD_DELIM
-                                           . 'CJK UNIFIED IDEOGRAPH';
+                                           . $name;
 
             }
             elsif ($fields[$CATEGORY] eq 'Co'
@@ -19193,7 +19202,8 @@ my @input_file_objects = (
                           . 'incorporated into the Unicode data base',
                    ),
     Input_file->new('StandardizedVariants.html', v3.2.0,
-                    Skip => 'Provides a visual display of the standard '
+                    Skip => 'Obsoleted as of Unicode 9.0, but previously '
+                          . 'provided a visual display of the standard '
                           . 'variant sequences derived from '
                           . 'F<StandardizedVariants.txt>.',
                         # I don't know why the html came earlier than the
@@ -19407,6 +19417,12 @@ my @input_file_objects = (
                     Property => 'Indic_Positional_Category',
                     Has_Missings_Defaults => $NOT_IGNORED,
                    ),
+    Input_file->new('TangutSources.txt', v9.0.0,
+                    Skip => 'Specifies source mappings for Tangut ideographs'
+                          . ' and components. This data file also includes'
+                          . ' informative radical-stroke values that are used'
+                          . ' internally by Unicode',
+                   ),
 );
 
 # End of all the preliminaries.
@@ -19871,7 +19887,10 @@ if (defined &locales_enabled) {
 }
 
 # Eval'd so can run on versions earlier than the property is available in
-my $WB_Extend_or_Format_re = eval 'qr/[\p{WB=Extend}\p{WB=Format}]/';
+my $WB_Extend_or_Format_re = eval 'qr/[\p{WB=Extend}\p{WB=Format}\p{WB=ZWJ}]/';
+if (! defined $WB_Extend_or_Format_re) {
+    $WB_Extend_or_Format_re = eval 'qr/[\p{WB=Extend}\p{WB=Format}]/';
+}
 
 sub _test_break($$) {
     # Test various break property matches.  The 2nd parameter gives the
index 3df9bd2..52d44ae 100644 (file)
@@ -623,7 +623,7 @@ If the final space character in the span is a horizontal white space, it
 is broken out so that it attaches instead to the combining character.
 To be precise, if a span of white space that ends in a horizontal space
 has the character immediately following it have either of the Word
-Boundary property values "Extend" or "Format", the boundary between the
+Boundary property values "Extend", "Format" or "ZWJ", the boundary between the
 final horizontal space character and the rest of the span matches
 C<\b{wb}>.  In all other cases the boundary between two white space
 characters matches C<\B{wb}>.)
diff --git a/proto.h b/proto.h
index 8b81e50..369da2c 100644 (file)
--- a/proto.h
+++ b/proto.h
@@ -5219,6 +5219,11 @@ STATIC WB_enum   S_advance_one_WB(pTHX_ U8 ** curpos, const U8 * const strend, con
 #define PERL_ARGS_ASSERT_ADVANCE_ONE_WB        \
        assert(curpos); assert(strend)
 
+STATIC GCB_enum        S_backup_one_GCB(pTHX_ const U8 * const strbeg, U8 ** curpos, const bool utf8_target)
+                       __attribute__warn_unused_result__;
+#define PERL_ARGS_ASSERT_BACKUP_ONE_GCB        \
+       assert(strbeg); assert(curpos)
+
 STATIC LB_enum S_backup_one_LB(pTHX_ const U8 * const strbeg, U8 ** curpos, const bool utf8_target)
                        __attribute__warn_unused_result__;
 #define PERL_ARGS_ASSERT_BACKUP_ONE_LB \
@@ -5247,8 +5252,10 @@ STATIC bool      S_isFOO_utf8_lc(pTHX_ const U8 classnum, const U8* character)
 #define PERL_ARGS_ASSERT_ISFOO_UTF8_LC \
        assert(character)
 
-PERL_STATIC_INLINE bool        S_isGCB(const GCB_enum before, const GCB_enum after)
+STATIC bool    S_isGCB(pTHX_ const GCB_enum before, const GCB_enum after, const U8 * const strbeg, const U8 * const curpos, const bool utf8_target)
                        __attribute__warn_unused_result__;
+#define PERL_ARGS_ASSERT_ISGCB \
+       assert(strbeg); assert(curpos)
 
 STATIC bool    S_isLB(pTHX_ LB_enum before, LB_enum after, const U8 * const strbeg, const U8 * const curpos, const U8 * const strend, const bool utf8_target)
                        __attribute__warn_unused_result__;
index d50a5c7..5e34e37 100644 (file)
 #endif /* H_REGCHARCLASS */
 
 /* Generated from:
- * 66726fe32be96a422e8c9b45bc9daf61e068d988c99ff41112972ef721365521 lib/Unicode/UCD.pm
+ * de6076d81bc4e85f179377ded4c68f3b257c8f7990227d4302eca442fda558f8 lib/Unicode/UCD.pm
  * ae98bec7e4f0564758eed81eca5015481ba32581f8a735a825b71b3bba714450 lib/unicore/ArabicShaping.txt
  * 1687fe5994eb7e5c0dab8503fc2a1b3b479d91af9d3b8055941c9bd791f7d0b5 lib/unicore/BidiBrackets.txt
  * 350d1302116194b0b21def287434b55c5088098fbc726e879f7420a391965643 lib/unicore/BidiMirroring.txt
  * 1a0687fb9c6c4567e853913549df0944fe40821279a3e9cdaa6ab8679bc286fd lib/unicore/extracted/DLineBreak.txt
  * 40bcfed3ca727c19e1331f6c33806231d5f7eeeabd2e6a9e06a3740c85d0c250 lib/unicore/extracted/DNumType.txt
  * a18d502bad39d527ac5586d7bc93e29f565859e3bcc24ada627eff606d6f5fed lib/unicore/extracted/DNumValues.txt
- * 45321b549a605b65ead1e83cdb90fdd9c5a6c8731a537197f335bab251b4e778 lib/unicore/mktables
+ * 4fbcc500e9215a31d39fa3fba793a4c893285e7d19912fc86fa6518120ecc4e1 lib/unicore/mktables
  * 462c9aaa608fb2014cd9649af1c5c009485c60b9c8b15b89401fdc10cf6161c6 lib/unicore/version
  * 913d2f93f3cb6cdf1664db888bf840bc4eb074eef824e082fceda24a9445e60c regen/charset_translations.pl
  * d9c04ac46bdd81bb3e26519f2b8eb6242cb12337205add3f7cf092b0c58dccc4 regen/regcharclass.pl
index 09d2961..460a72c 100644 (file)
@@ -72,7 +72,11 @@ my %hard_coded_enums =
  ( gcb => [
             'Control',
             'CR',
+            'E_Base',
+            'E_Base_GAZ',
+            'E_Modifier',
             'Extend',
+            'Glue_After_Zwj',
             'L',
             'LF',
             'LV',
@@ -83,6 +87,7 @@ my %hard_coded_enums =
             'SpacingMark',
             'T',
             'V',
+            'ZWJ',
         ],
     lb => [
             'Alphabetic',
@@ -95,6 +100,8 @@ my %hard_coded_enums =
             'Close_Punctuation',
             'Combining_Mark',
             'Contingent_Break',
+            'E_Base',
+            'E_Modifier',
             'Exclamation',
             'Glue',
             'H2',
@@ -119,6 +126,7 @@ my %hard_coded_enums =
             'Regional_Indicator',
             'Space',
             'Word_Joiner',
+            'ZWJ',
             'ZWSpace',
         ],
    sb  => [
@@ -142,9 +150,13 @@ my %hard_coded_enums =
             'ALetter',
             'CR',
             'Double_Quote',
+            'E_Base',
+            'E_Base_GAZ',
+            'E_Modifier',
             'Extend',
             'ExtendNumLet',
             'Format',
+            'Glue_After_Zwj',
             'Hebrew_Letter',
             'Katakana',
             'LF',
@@ -157,6 +169,7 @@ my %hard_coded_enums =
             'Perl_Tailored_HSpace',
             'Regional_Indicator',
             'Single_Quote',
+            'ZWJ',
         ],
 );
 
@@ -720,6 +733,12 @@ sub output_GCB_table() {
 
     # Create and output the pair table for use in determining Grapheme Cluster
     # Breaks, given in http://www.unicode.org/reports/tr29/.
+    my %gcb_actions = (
+        GCB_NOBREAK                      => 0,
+        GCB_BREAKABLE                    => 1,
+        GCB_RI_then_RI                   => 2,   # Rules 12 and 13
+        GCB_EX_then_EM                   => 3,   # Rule 10
+    );
 
     # The table is constructed in reverse order of the rules, to make the
     # lower-numbered, higher priority ones override the later ones, as the
@@ -729,29 +748,45 @@ sub output_GCB_table() {
     my $table_size = @gcb_short_enums;
 
     # Otherwise, break everywhere.
-    # GB10     Any ÷  Any
+    # GB99   Any ÷  Any
     for my $i (0 .. $table_size - 1) {
         for my $j (0 .. $table_size - 1) {
             $gcb_table[$i][$j] = 1;
         }
     }
 
-    # Do not break before extending characters.
+    # Do not break within emoji flag sequences. That is, do not break between
+    # regional indicator (RI) symbols if there is an odd number of RI
+    # characters before the break point.  Must be resolved in runtime code.
+    #
+    # GB12 ^ (RI RI)* RI × RI
+    # GB13 [^RI] (RI RI)* RI × RI
+    $gcb_table[$gcb_enums{'Regional_Indicator'}]
+              [$gcb_enums{'Regional_Indicator'}] = $gcb_actions{GCB_RI_then_RI};
+
+    # Do not break within emoji modifier sequences or emoji zwj sequences.
+    # GB11  ZWJ  × ( Glue_After_Zwj | E_Base_GAZ )
+    $gcb_table[$gcb_enums{'ZWJ'}][$gcb_enums{'Glue_After_Zwj'}] = 0;
+    $gcb_table[$gcb_enums{'ZWJ'}][$gcb_enums{'E_Base_GAZ'}] = 0;
+
+    # GB10  ( E_Base | E_Base_GAZ ) Extend* ×  E_Modifier
+    $gcb_table[$gcb_enums{'Extend'}][$gcb_enums{'E_Modifier'}]
+                                                = $gcb_actions{GCB_EX_then_EM};
+    $gcb_table[$gcb_enums{'E_Base'}][$gcb_enums{'E_Modifier'}] = 0;
+    $gcb_table[$gcb_enums{'E_Base_GAZ'}][$gcb_enums{'E_Modifier'}] = 0;
+
+    # Do not break before extending characters or ZWJ.
     # Do not break before SpacingMarks, or after Prepend characters.
-    # GB9   ×  Extend
-    # GB9a  × SpacingMark
     # GB9b  Prepend  ×
+    # GB9a  × SpacingMark
+    # GB9   ×  ( Extend | ZWJ )
     for my $i (0 .. @gcb_table - 1) {
-        $gcb_table[$i][$gcb_enums{'Extend'}] = 0;
-        $gcb_table[$i][$gcb_enums{'SpacingMark'}] = 0;
         $gcb_table[$gcb_enums{'Prepend'}][$i] = 0;
+        $gcb_table[$i][$gcb_enums{'SpacingMark'}] = 0;
+        $gcb_table[$i][$gcb_enums{'Extend'}] = 0;
+        $gcb_table[$i][$gcb_enums{'ZWJ'}] = 0;
     }
 
-    # Do not break between regional indicator symbols.
-    # GB8a  Regional_Indicator  ×  Regional_Indicator
-    $gcb_table[$gcb_enums{'Regional_Indicator'}]
-              [$gcb_enums{'Regional_Indicator'}] = 0;
-
     # Do not break Hangul syllable sequences.
     # GB8  ( LVT | T)  ×  T
     $gcb_table[$gcb_enums{'LVT'}][$gcb_enums{'T'}] = 0;
@@ -785,18 +820,16 @@ sub output_GCB_table() {
     # GB3  CR  ×  LF
     $gcb_table[$gcb_enums{'CR'}][$gcb_enums{'LF'}] = 0;
 
-    # Break at the start and end of text.
+    # Break at the start and end of text, unless the text is empty
     # GB1  sot  ÷
     # GB2   ÷  eot
     for my $i (0 .. @gcb_table - 1) {
         $gcb_table[$i][$gcb_enums{'EDGE'}] = 1;
         $gcb_table[$gcb_enums{'EDGE'}][$i] = 1;
     }
-
-    # But, unspecified by Unicode, we shouldn't break on an empty string.
     $gcb_table[$gcb_enums{'EDGE'}][$gcb_enums{'EDGE'}] = 0;
 
-    output_table_common('GCB', undef,
+    output_table_common('GCB', \%gcb_actions,
                         \@gcb_table, \@gcb_short_enums, \%gcb_abbreviations);
 }
 
@@ -828,13 +861,14 @@ sub output_LB_table() {
         LB_BREAKABLE                    => 1,
         LB_NOBREAK_EVEN_WITH_SP_BETWEEN => 2,
 
-        LB_CM_foo                       => 3,   # Rule 9
+        LB_CM_ZWJ_foo                   => 3,   # Rule 9
         LB_SP_foo                       => 6,   # Rule 18
         LB_PR_or_PO_then_OP_or_HY       => 9,   # Rule 25
         LB_SY_or_IS_then_various        => 11,  # Rule 25
         LB_HY_or_BA_then_foo            => 13,  # Rule 21
+        LB_RI_then_RI                  => 15,  # Rule 30a
 
-        LB_various_then_PO_or_PR        => (1<<4),  # Rule 25
+        LB_various_then_PO_or_PR        => (1<<5),  # Rule 25
     );
 
     # Construct the LB pair table.  This is based on the rules in
@@ -858,9 +892,18 @@ sub output_LB_table() {
         }
     }
 
-    # LB30a. Don't break between Regional Indicators
+    # LB30b Do not break between an emoji base and an emoji modifier.
+    # EB × EM
+    $lb_table[$lb_enums{'E_Base'}][$lb_enums{'E_Modifier'}]
+                                                = $lb_actions{'LB_NOBREAK'};
+
+    # LB30a Break between two regional indicator symbols if and only if there
+    # are an even number of regional indicators preceding the position of the
+    # break.
+    # sot (RI RI)* RI × RI
+    # [^RI] (RI RI)* RI × RI
     $lb_table[$lb_enums{'Regional_Indicator'}]
-             [$lb_enums{'Regional_Indicator'}] = $lb_actions{'LB_NOBREAK'};
+             [$lb_enums{'Regional_Indicator'}] = $lb_actions{'LB_RI_then_RI'};
 
     # LB30 Do not break between letters, numbers, or ordinary symbols and
     # opening or closing parentheses.
@@ -1046,28 +1089,47 @@ sub output_LB_table() {
     $lb_table[$lb_enums{'Break_Symbols'}][$lb_enums{'Prefix_Numeric'}]
                                     += $lb_actions{'LB_various_then_PO_or_PR'};
 
-    # LB24 Do not break between prefix and letters or ideographs.
-    # PR × ID
-    $lb_table[$lb_enums{'Prefix_Numeric'}][$lb_enums{'Ideographic'}]
-                                                = $lb_actions{'LB_NOBREAK'};
-
-    # PR × (AL | HL)
+    # LB24 Do not break between numeric prefix/postfix and letters, or between
+    # letters and prefix/postfix.
+    # (PR | PO) × (AL | HL)
     $lb_table[$lb_enums{'Prefix_Numeric'}][$lb_enums{'Alphabetic'}]
                                                 = $lb_actions{'LB_NOBREAK'};
     $lb_table[$lb_enums{'Prefix_Numeric'}][$lb_enums{'Hebrew_Letter'}]
                                                 = $lb_actions{'LB_NOBREAK'};
-
-    # PO × (AL | HL)
     $lb_table[$lb_enums{'Postfix_Numeric'}][$lb_enums{'Alphabetic'}]
                                                 = $lb_actions{'LB_NOBREAK'};
     $lb_table[$lb_enums{'Postfix_Numeric'}][$lb_enums{'Hebrew_Letter'}]
                                                 = $lb_actions{'LB_NOBREAK'};
 
-    # LB23 Do not break within ‘a9’, ‘3a’, or ‘H%’.
-    # ID × PO
+    # (AL | HL) × (PR | PO)
+    $lb_table[$lb_enums{'Alphabetic'}][$lb_enums{'Prefix_Numeric'}]
+                                                = $lb_actions{'LB_NOBREAK'};
+    $lb_table[$lb_enums{'Hebrew_Letter'}][$lb_enums{'Prefix_Numeric'}]
+                                                = $lb_actions{'LB_NOBREAK'};
+    $lb_table[$lb_enums{'Alphabetic'}][$lb_enums{'Postfix_Numeric'}]
+                                                = $lb_actions{'LB_NOBREAK'};
+    $lb_table[$lb_enums{'Hebrew_Letter'}][$lb_enums{'Postfix_Numeric'}]
+                                                = $lb_actions{'LB_NOBREAK'};
+
+    # LB23a Do not break between numeric prefixes and ideographs, or between
+    # ideographs and numeric postfixes.
+    # PR × (ID | EB | EM)
+    $lb_table[$lb_enums{'Prefix_Numeric'}][$lb_enums{'Ideographic'}]
+                                                = $lb_actions{'LB_NOBREAK'};
+    $lb_table[$lb_enums{'Prefix_Numeric'}][$lb_enums{'E_Base'}]
+                                                = $lb_actions{'LB_NOBREAK'};
+    $lb_table[$lb_enums{'Prefix_Numeric'}][$lb_enums{'E_Modifier'}]
+                                                = $lb_actions{'LB_NOBREAK'};
+
+    # (ID | EB | EM) × PO
     $lb_table[$lb_enums{'Ideographic'}][$lb_enums{'Postfix_Numeric'}]
                                                 = $lb_actions{'LB_NOBREAK'};
+    $lb_table[$lb_enums{'E_Base'}][$lb_enums{'Postfix_Numeric'}]
+                                                = $lb_actions{'LB_NOBREAK'};
+    $lb_table[$lb_enums{'E_Modifier'}][$lb_enums{'Postfix_Numeric'}]
+                                                = $lb_actions{'LB_NOBREAK'};
 
+    # LB23 Do not break between digits and letters
     # (AL | HL) × NU
     $lb_table[$lb_enums{'Alphabetic'}][$lb_enums{'Numeric'}]
                                                 = $lb_actions{'LB_NOBREAK'};
@@ -1092,9 +1154,13 @@ sub output_LB_table() {
     $lb_table[$lb_enums{'Exclamation'}][$lb_enums{'Inseparable'}]
                                                 = $lb_actions{'LB_NOBREAK'};
 
-    # ID × IN
+    # (ID | EB | EM) × IN
     $lb_table[$lb_enums{'Ideographic'}][$lb_enums{'Inseparable'}]
                                                 = $lb_actions{'LB_NOBREAK'};
+    $lb_table[$lb_enums{'E_Base'}][$lb_enums{'Inseparable'}]
+                                                = $lb_actions{'LB_NOBREAK'};
+    $lb_table[$lb_enums{'E_Modifier'}][$lb_enums{'Inseparable'}]
+                                                = $lb_actions{'LB_NOBREAK'};
 
     # IN × IN
     $lb_table[$lb_enums{'Inseparable'}][$lb_enums{'Inseparable'}]
@@ -1256,19 +1322,22 @@ sub output_LB_table() {
     #
     # LB9 Do not break a combining character sequence; treat it as if it has
     # the line breaking class of the base character in all of the
-    # higher-numbered rules.
-    # Treat X CM* as if it were X.
+    # higher-numbered rules.  Treat ZWJ as if it were CM
+    # Treat X (CM|ZWJ)* as if it were X.
     # where X is any line break class except BK, CR, LF, NL, SP, or ZW.
 
-    # LB10 Treat any remaining combining mark as AL.  This catches the case
-    # where a CM is the first character on the line or follows SP, BK, CR, LF,
-    # NL, or ZW.
+    # LB10 Treat any remaining combining mark or ZWJ as AL.  This catches the
+    # case where a CM or ZWJ is the first character on the line or follows SP,
+    # BK, CR, LF, NL, or ZW.
     for my $i (0 .. @lb_table - 1) {
 
-        # When the CM is the first in the pair, we don't know without looking
-        # behind whether the CM is going to inherit from an earlier character,
-        # or not.  So have to figure this out in the code
-        $lb_table[$lb_enums{'Combining_Mark'}][$i] = $lb_actions{'LB_CM_foo'};
+        # When the CM or ZWJ is the first in the pair, we don't know without
+        # looking behind whether the CM or ZWJ is going to attach to an
+        # earlier character, or not.  So have to figure this out at runtime in
+        # the code
+        $lb_table[$lb_enums{'Combining_Mark'}][$i]
+                                        = $lb_actions{'LB_CM_ZWJ_foo'};
+        $lb_table[$lb_enums{'ZWJ'}][$i] = $lb_actions{'LB_CM_ZWJ_foo'};
 
         if (   $i == $lb_enums{'Mandatory_Break'}
             || $i == $lb_enums{'EDGE'}
@@ -1282,19 +1351,38 @@ sub output_LB_table() {
             # whatever 'Alphabetic' would do.
             $lb_table[$i][$lb_enums{'Combining_Mark'}]
                                     = $lb_table[$i][$lb_enums{'Alphabetic'}];
+            $lb_table[$i][$lb_enums{'ZWJ'}]
+                                    = $lb_table[$i][$lb_enums{'Alphabetic'}];
         }
         else {
-            # For these classes, the CM combines, so doesn't break, inheriting
-            # the type of nobreak from the master character.
+            # For these classes, the CM or ZWJ combines, so doesn't break,
+            # inheriting the type of nobreak from the master character.
             if ($lb_table[$i][$lb_enums{'Combining_Mark'}]
                             != $lb_actions{'LB_NOBREAK_EVEN_WITH_SP_BETWEEN'})
             {
                 $lb_table[$i][$lb_enums{'Combining_Mark'}]
                                         = $lb_actions{'LB_NOBREAK'};
             }
+            if ($lb_table[$i][$lb_enums{'ZWJ'}]
+                            != $lb_actions{'LB_NOBREAK_EVEN_WITH_SP_BETWEEN'})
+            {
+                $lb_table[$i][$lb_enums{'ZWJ'}]
+                                        = $lb_actions{'LB_NOBREAK'};
+            }
         }
     }
 
+    # LB8a Do not break between a zero width joiner and an ideograph, emoji
+    # base or emoji modifier. This rule prevents breaks within emoji joiner
+    # sequences.
+    # ZWJ × (ID | EB | EM)
+    $lb_table[$lb_enums{'ZWJ'}][$lb_enums{'Ideographic'}]
+                                                    = $lb_actions{'LB_NOBREAK'};
+    $lb_table[$lb_enums{'ZWJ'}][$lb_enums{'E_Base'}]
+                                                    = $lb_actions{'LB_NOBREAK'};
+    $lb_table[$lb_enums{'ZWJ'}][$lb_enums{'E_Modifier'}]
+                                                    = $lb_actions{'LB_NOBREAK'};
+
     # LB8 Break before any character following a zero-width space, even if one
     # or more spaces intervene.
     # ZW SP* ÷
@@ -1349,12 +1437,10 @@ sub output_LB_table() {
                                 = $lb_actions{'LB_BREAKABLE'};
     }
 
-    # LB2 Never break at the start of text.
-    # sot ×
     # LB3 Always break at the end of text.
     # ! eot
-    # but these are reversed in the loop below, so that won't break if there
-    # is no text
+    # LB2 Never break at the start of text.
+    # sot ×
     for my $i (0 .. @lb_table - 1) {
         $lb_table[$i][$lb_enums{'EDGE'}] = $lb_actions{'LB_BREAKABLE'};
         $lb_table[$lb_enums{'EDGE'}][$i] = $lb_actions{'LB_NOBREAK'};
@@ -1393,13 +1479,14 @@ sub output_WB_table() {
         WB_NOBREAK                      => 0,
         WB_BREAKABLE                    => 1,
         WB_hs_then_hs                   => 2,
-        WB_Ex_or_FO_then_foo           => 3,
+        WB_Ex_or_FO_or_ZWJ_then_foo    => 3,
         WB_DQ_then_HL                  => 4,
         WB_HL_then_DQ                  => 6,
         WB_LE_or_HL_then_MB_or_ML_or_SQ        => 8,
         WB_MB_or_ML_or_SQ_then_LE_or_HL        => 10,
         WB_MB_or_MN_or_SQ_then_NU      => 12,
         WB_NU_then_MB_or_MN_or_SQ      => 14,
+        WB_RI_then_RI                  => 16,
     );
 
     # Construct the WB pair table.
@@ -1411,17 +1498,27 @@ sub output_WB_table() {
     my $table_size = @wb_short_enums - 1;   # -1 because we don't use UNKNOWN
 
     # Otherwise, break everywhere (including around ideographs).
-    # WB14  Any  ÷  Any
+    # WB99  Any  ÷  Any
     for my $i (0 .. $table_size - 1) {
         for my $j (0 .. $table_size - 1) {
             $wb_table[$i][$j] = $wb_actions{'WB_BREAKABLE'};
         }
     }
 
-    # Do not break between regional indicator symbols.
-    # WB13c  Regional_Indicator  ×  Regional_Indicator
+    # Do not break within emoji flag sequences. That is, do not break between
+    # regional indicator (RI) symbols if there is an odd number of RI
+    # characters before the break point.
+    # WB16  [^RI] (RI RI)* RI × RI
+    # WB15   ^    (RI RI)* RI × RI
     $wb_table[$wb_enums{'Regional_Indicator'}]
-             [$wb_enums{'Regional_Indicator'}] = $wb_actions{'WB_NOBREAK'};
+             [$wb_enums{'Regional_Indicator'}] = $wb_actions{'WB_RI_then_RI'};
+
+    # Do not break within emoji modifier sequences.
+    # WB14  ( E_Base | EBG )  ×  E_Modifier
+    $wb_table[$wb_enums{'E_Base'}][$wb_enums{'E_Modifier'}]
+                                                    = $wb_actions{'WB_NOBREAK'};
+    $wb_table[$wb_enums{'E_Base_GAZ'}][$wb_enums{'E_Modifier'}]
+                                                    = $wb_actions{'WB_NOBREAK'};
 
     # Do not break from extenders.
     # WB13b  ExtendNumLet  ×  (ALetter | Hebrew_Letter | Numeric | Katakana)
@@ -1541,14 +1638,21 @@ sub output_WB_table() {
     $wb_table[$wb_enums{'Hebrew_Letter'}][$wb_enums{'Hebrew_Letter'}]
                                                     = $wb_actions{'WB_NOBREAK'};
 
-    # Ignore Format and Extend characters, except when they appear at the
-    # beginning of a region of text.
-    # WB4  X (Extend | Format)*  →  X
+    # Ignore Format and Extend characters, except after sot, CR, LF, and
+    # Newline.  This also has the effect of: Any × (Format | Extend | ZWJ)
+    # WB4  X (Extend | Format | ZWJ)* → X
     for my $i (0 .. @wb_table - 1) {
         $wb_table[$wb_enums{'Extend'}][$i]
-                                        = $wb_actions{'WB_Ex_or_FO_then_foo'};
+                                = $wb_actions{'WB_Ex_or_FO_or_ZWJ_then_foo'};
         $wb_table[$wb_enums{'Format'}][$i]
-                                        = $wb_actions{'WB_Ex_or_FO_then_foo'};
+                                = $wb_actions{'WB_Ex_or_FO_or_ZWJ_then_foo'};
+        $wb_table[$wb_enums{'ZWJ'}][$i]
+                                = $wb_actions{'WB_Ex_or_FO_or_ZWJ_then_foo'};
+    }
+    for my $i (0 .. @wb_table - 1) {
+        $wb_table[$i][$wb_enums{'Extend'}] = $wb_actions{'WB_NOBREAK'};
+        $wb_table[$i][$wb_enums{'Format'}] = $wb_actions{'WB_NOBREAK'};
+        $wb_table[$i][$wb_enums{'ZWJ'}]    = $wb_actions{'WB_NOBREAK'};
     }
 
     # Implied is that these attach to the character before them, except for
@@ -1560,6 +1664,13 @@ sub output_WB_table() {
         $wb_table[$i][$wb_enums{'Format'}] = $wb_actions{'WB_NOBREAK'};
     }
 
+    # Do not break within emoji zwj sequences.
+    # WB3c ZWJ × ( Glue_After_Zwj | EBG )
+    $wb_table[$wb_enums{'ZWJ'}][$wb_enums{'Glue_After_Zwj'}]
+                                                = $wb_actions{'WB_NOBREAK'};
+    $wb_table[$wb_enums{'ZWJ'}][$wb_enums{'E_Base_GAZ'}]
+                                                = $wb_actions{'WB_NOBREAK'};
+
     # Break before and after white space
     # WB3b     ÷  (Newline | CR | LF)
     # WB3a  (Newline | CR | LF)  ÷
@@ -1580,24 +1691,24 @@ sub output_WB_table() {
         }
     }
 
-    # And do not break horizontal space followed by Extend or Format
+    # And do not break horizontal space followed by Extend or Format or ZWJ
     $wb_table[$wb_enums{'Perl_Tailored_HSpace'}][$wb_enums{'Extend'}]
                                                     = $wb_actions{'WB_NOBREAK'};
     $wb_table[$wb_enums{'Perl_Tailored_HSpace'}][$wb_enums{'Format'}]
                                                     = $wb_actions{'WB_NOBREAK'};
+    $wb_table[$wb_enums{'Perl_Tailored_HSpace'}][$wb_enums{'ZWJ'}]
+                                                    = $wb_actions{'WB_NOBREAK'};
     $wb_table[$wb_enums{'Perl_Tailored_HSpace'}]
               [$wb_enums{'Perl_Tailored_HSpace'}]
                                                 = $wb_actions{'WB_hs_then_hs'};
 
-    # Break at the start and end of text.
-    # WB2     ÷  eot
-    # WB1  sot  ÷
+    # Break at the start and end of text, unless the text is empty
+    # WB2  Any  ÷  eot
+    # WB1  sot  ÷  Any
     for my $i (0 .. @wb_table - 1) {
         $wb_table[$i][$wb_enums{'EDGE'}] = $wb_actions{'WB_BREAKABLE'};
         $wb_table[$wb_enums{'EDGE'}][$i] = $wb_actions{'WB_BREAKABLE'};
     }
-
-    # But, unspecified by Unicode, we shouldn't break on an empty string.
     $wb_table[$wb_enums{'EDGE'}][$wb_enums{'EDGE'}] = 0;
 
     output_table_common('WB', \%wb_actions,
index fdcffc5..448a605 100644 (file)
--- a/regexec.c
+++ b/regexec.c
@@ -2118,7 +2118,11 @@ S_find_byclass(pTHX_ regexp * prog, const regnode *c, char *s,
                     while (s < strend) {
                         GCB_enum after = getGCB_VAL_UTF8((U8*) s,
                                                         (U8*) reginfo->strend);
-                        if (   (to_complement ^ isGCB(before, after))
+                        if (   (to_complement ^ isGCB(before,
+                                                      after,
+                                                      (U8*) reginfo->strbeg,
+                                                      (U8*) s,
+                                                      utf8_target))
                             && (reginfo->intuit || regtry(reginfo, &s)))
                         {
                             goto got_it;
@@ -4289,13 +4293,108 @@ S_setup_EXACTISH_ST_c1_c2(pTHX_ const regnode * const text_node, int *c1p,
     return TRUE;
 }
 
-PERL_STATIC_INLINE bool
-S_isGCB(const GCB_enum before, const GCB_enum after)
+STATIC bool
+S_isGCB(pTHX_ const GCB_enum before, const GCB_enum after, const U8 * const strbeg, const U8 * curpos, const bool utf8_target)
 {
     /* returns a boolean indicating if there is a Grapheme Cluster Boundary
-     * between the inputs.  See http://www.unicode.org/reports/tr29/ */
+     * between the inputs.  See http://www.unicode.org/reports/tr29/. */
+
+    PERL_ARGS_ASSERT_ISGCB;
+
+    switch (GCB_table[before][after]) {
+        case GCB_BREAKABLE:
+            return TRUE;
+
+        case GCB_NOBREAK:
+            return FALSE;
+
+        case GCB_RI_then_RI:
+            {
+                int RI_count = 1;
+                U8 * temp_pos = (U8 *) curpos;
+
+                /* Do not break within emoji flag sequences. That is, do not
+                 * break between regional indicator (RI) symbols if there is an
+                 * odd number of RI characters before the break point.
+                 *  GB12     ^ (RI RI)* RI × RI
+                 *  GB13 [^RI] (RI RI)* RI × RI */
+
+                while (backup_one_GCB(strbeg,
+                                    &temp_pos,
+                                    utf8_target) == GCB_Regional_Indicator)
+                {
+                    RI_count++;
+                }
+
+                return RI_count % 2 != 1;
+            }
+
+        case GCB_EX_then_EM:
+
+            /* GB10  ( E_Base | E_Base_GAZ ) Extend* ×  E_Modifier */
+            {
+                U8 * temp_pos = (U8 *) curpos;
+                GCB_enum prev;
+
+                do {
+                    prev = backup_one_GCB(strbeg, &temp_pos, utf8_target);
+                }
+                while (prev == GCB_Extend);
+
+                return prev != GCB_E_Base && prev != GCB_E_Base_GAZ;
+            }
 
-    return GCB_table[before][after];
+        default:
+            break;
+    }
+
+#ifdef DEBUGGING
+    Perl_re_printf( aTHX_  "Unhandled GCB pair: GCB_table[%d, %d] = %d\n",
+                                  before, after, GCB_table[before][after]);
+    assert(0);
+#endif
+    return TRUE;
+}
+
+STATIC GCB_enum
+S_backup_one_GCB(pTHX_ const U8 * const strbeg, U8 ** curpos, const bool utf8_target)
+{
+    GCB_enum gcb;
+
+    PERL_ARGS_ASSERT_BACKUP_ONE_GCB;
+
+    if (*curpos < strbeg) {
+        return GCB_EDGE;
+    }
+
+    if (utf8_target) {
+        U8 * prev_char_pos = reghopmaybe3(*curpos, -1, strbeg);
+        U8 * prev_prev_char_pos;
+
+        if (! prev_char_pos) {
+            return GCB_EDGE;
+        }
+
+        if ((prev_prev_char_pos = reghopmaybe3((U8 *) prev_char_pos, -1, strbeg))) {
+            gcb = getGCB_VAL_UTF8(prev_prev_char_pos, prev_char_pos);
+            *curpos = prev_char_pos;
+            prev_char_pos = prev_prev_char_pos;
+        }
+        else {
+            *curpos = (U8 *) strbeg;
+            return GCB_EDGE;
+        }
+    }
+    else {
+        if (*curpos - 2 < strbeg) {
+            *curpos = (U8 *) strbeg;
+            return GCB_EDGE;
+        }
+        (*curpos)--;
+        gcb = getGCB_VAL_CP(*(*curpos - 1));
+    }
+
+    return gcb;
 }
 
 /* Combining marks attach to most classes that precede them, but this defines
@@ -4326,7 +4425,7 @@ S_isLB(pTHX_ LB_enum before,
 
     PERL_ARGS_ASSERT_ISLB;
 
-    /* Rule numbers in the comments below are as of Unicode 8.0 */
+    /* Rule numbers in the comments below are as of Unicode 9.0 */
 
   redo:
     before = prev;
@@ -4420,14 +4519,14 @@ S_isLB(pTHX_ LB_enum before,
              * that is overriden */
             return LB_table[prev][after] != LB_NOBREAK_EVEN_WITH_SP_BETWEEN;
 
-        case LB_CM_foo:
+        case LB_CM_ZWJ_foo:
 
             /* We don't know how to treat the CM except by looking at the first
-             * non-CM character preceding it */
+             * non-CM character preceding it.  ZWJ is treated as CM */
             do {
                 prev = backup_one_LB(strbeg, &temp_pos, utf8_target);
             }
-            while (prev == LB_Combining_Mark);
+            while (prev == LB_Combining_Mark || prev == LB_ZWJ);
 
             /* Here, 'prev' is that first earlier non-CM character.  If the CM
              * attatches to it, then it inherits the behavior of 'prev'.  If it
@@ -4500,6 +4599,28 @@ S_isLB(pTHX_ LB_enum before,
             return LB_various_then_PO_or_PR;
         }
 
+        case LB_RI_then_RI + LB_NOBREAK:
+        case LB_RI_then_RI + LB_BREAKABLE:
+            {
+                int RI_count = 1;
+
+                /* LB30a Break between two regional indicator symbols if and
+                 * only if there are an even number of regional indicators
+                 * preceding the position of the break.
+                 *
+                 *  sot (RI RI)* RI × RI
+                 *  [^RI] (RI RI)* RI × RI */
+
+                while (backup_one_LB(strbeg,
+                                     &temp_pos,
+                                     utf8_target) == LB_Regional_Indicator)
+                {
+                    RI_count++;
+                }
+
+                return RI_count % 2 == 0;
+            }
+
         default:
             break;
     }
@@ -4884,7 +5005,7 @@ S_isWB(pTHX_ WB_enum previous,
 
     PERL_ARGS_ASSERT_ISWB;
 
-    /* Rule numbers in the comments below are as of Unicode 8.0 */
+    /* Rule numbers in the comments below are as of Unicode 9.0 */
 
   redo:
     before = prev;
@@ -4910,11 +5031,11 @@ S_isWB(pTHX_ WB_enum previous,
          * the beginning of a region of text', the rule is to break before
          * them, just like any other character.  Therefore, the default rule
          * applies and we don't have to look in more depth.  Should this ever
-         * change, we would have to have 2 'case' statements, like in the
-         * rules below, and backup a single character (not spacing over the
-         * extend ones) and then see if that is one of the region-end
-         * characters and go from there */
-        case WB_Ex_or_FO_then_foo:
+         * change, we would have to have 2 'case' statements, like in the rules
+         * below, and backup a single character (not spacing over the extend
+         * ones) and then see if that is one of the region-end characters and
+         * go from there */
+        case WB_Ex_or_FO_or_ZWJ_then_foo:
             prev = backup_one_WB(&previous, strbeg, &before_pos, utf8_target);
             goto redo;
 
@@ -5007,6 +5128,30 @@ S_isWB(pTHX_ WB_enum previous,
             return WB_table[before][after]
                                 - WB_NU_then_MB_or_MN_or_SQ == WB_BREAKABLE;
 
+        case WB_RI_then_RI + WB_NOBREAK:
+        case WB_RI_then_RI + WB_BREAKABLE:
+            {
+                int RI_count = 1;
+
+                /* Do not break within emoji flag sequences. That is, do not
+                 * break between regional indicator (RI) symbols if there is an
+                 * odd number of RI characters before the potential break
+                 * point.
+                 *
+                 * WB15     ^ (RI RI)* RI × RI
+                 * WB16 [^RI] (RI RI)* RI × RI */
+
+                while (backup_one_WB(&previous,
+                                     strbeg,
+                                     &before_pos,
+                                     utf8_target) == WB_Regional_Indicator)
+                {
+                    RI_count++;
+                }
+
+                return RI_count % 2 != 1;
+            }
+
         default:
             break;
     }
@@ -5087,8 +5232,8 @@ S_backup_one_WB(pTHX_ WB_enum * previous, const U8 * const strbeg, U8 ** curpos,
             *previous = (*curpos <= strbeg) ? WB_EDGE : WB_UNKNOWN;
         }
 
-        /* And we always back up over these two types */
-        if (wb != WB_Extend && wb != WB_Format) {
+        /* And we always back up over these three types */
+        if (wb != WB_Extend && wb != WB_Format && wb != WB_ZWJ) {
             return wb;
         }
     }
@@ -5119,7 +5264,7 @@ S_backup_one_WB(pTHX_ WB_enum * previous, const U8 * const strbeg, U8 ** curpos,
                 *curpos = (U8 *) strbeg;
                 return WB_EDGE;
             }
-        } while (wb == WB_Extend || wb == WB_Format);
+        } while (wb == WB_Extend || wb == WB_Format || wb == WB_ZWJ);
     }
     else {
         do {
@@ -6001,7 +6146,10 @@ S_regmatch(pTHX_ regmatch_info *reginfo, char *startpos, regnode *prog)
                                                         (U8*)(reginfo->strbeg)),
                                                 (U8*) reginfo->strend),
                                           getGCB_VAL_UTF8((U8*) locinput,
-                                                        (U8*) reginfo->strend));
+                                                        (U8*) reginfo->strend),
+                                          (U8*) reginfo->strbeg,
+                                          (U8*) locinput,
+                                          utf8_target);
                         }
                         break;
 
@@ -6383,7 +6531,10 @@ S_regmatch(pTHX_ regmatch_info *reginfo, char *startpos, regnode *prog)
                 while (locinput < reginfo->strend) {
                     GCB_enum cur_gcb = getGCB_VAL_UTF8((U8*) locinput,
                                                          (U8*) reginfo->strend);
-                    if (isGCB(prev_gcb, cur_gcb)) {
+                    if (isGCB(prev_gcb, cur_gcb,
+                              (U8*) reginfo->strbeg, (U8*) locinput,
+                              utf8_target))
+                    {
                         break;
                     }