This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Merge topic branch into blead
authorKarl Williamson <public@khwilliamson.com>
Tue, 20 Nov 2012 00:13:41 +0000 (17:13 -0700)
committerKarl Williamson <public@khwilliamson.com>
Tue, 20 Nov 2012 00:13:41 +0000 (17:13 -0700)
This branch continues to make more systematic the handy.h definitions of
isFOO() macros started in 53335a31ed2284e8aaf1b6bf1cdcc0c0357841d0,
fixing some bugs and some incidentals found along the way.

Four of these macros are changed to not use function calls for
characters outside the Latin1 range.  Three of these used interpreter
level variables.  All core uses of these three interpreter variables
are removed in this series of commits, so the variables could be removed
without affecting the core.

18 files changed:
embed.fnc
embed.h
ext/XS-APItest/APItest.pm
ext/XS-APItest/APItest.xs
ext/XS-APItest/t/handy.t
handy.h
l1_char_class_tab.h
lib/Unicode/UCD.pm
proto.h
regcharclass.h
regcomp.c
regcomp.h
regcomp.sym
regen/mk_PL_charclass.pl
regen/regcharclass.pl
regexec.c
regnodes.h
utf8.c

index 9aec97a..d659737 100644 (file)
--- a/embed.fnc
+++ b/embed.fnc
@@ -614,6 +614,8 @@ sR  |U8     |to_lower_latin1|const U8 c|NULLOK U8 *p|NULLOK STRLEN *lenp
 #endif
 #if defined(PERL_IN_UTF8_C) || defined(PERL_IN_REGCOMP_C) || defined(PERL_IN_REGEXEC_C)
 EXp        |UV        |_to_fold_latin1|const U8 c|NN U8 *p|NN STRLEN *lenp|const bool flags
+EMXpR  |bool   |is_utf8_X_extend       |NN const U8 *p
+EMXpR  |bool   |is_utf8_X_regular_begin|NN const U8 *p
 #endif
 #if defined(PERL_IN_UTF8_C) || defined(PERL_IN_PP_C)
 p      |UV     |_to_upper_title_latin1|const U8 c|NN U8 *p|NN STRLEN *lenp|const char S_or_s
@@ -643,7 +645,7 @@ Anpd        |bool   |is_utf8_string_loclen|NN const U8 *s|STRLEN len|NULLOK const U8 **ep
 ApR    |bool   |is_utf8_alnum  |NN const U8 *p
 ApR    |bool   |is_utf8_idfirst|NN const U8 *p
 ApR    |bool   |is_utf8_xidfirst|NN const U8 *p
-EXpR   |bool   |_is_utf8__perl_idstart|NN const U8 *p
+ApR    |bool   |_is_utf8__perl_idstart|NN const U8 *p
 ApR    |bool   |is_utf8_idcont |NN const U8 *p
 ApR    |bool   |is_utf8_xidcont        |NN const U8 *p
 ApR    |bool   |is_utf8_alpha  |NN const U8 *p
@@ -662,8 +664,6 @@ ApR |bool   |is_utf8_print  |NN const U8 *p
 ApR    |bool   |is_utf8_punct  |NN const U8 *p
 ApR    |bool   |is_utf8_xdigit |NN const U8 *p
 ApR    |bool   |is_utf8_mark   |NN const U8 *p
-EXpR   |bool   |is_utf8_X_extend       |NN const U8 *p
-EXpR   |bool   |is_utf8_X_regular_begin|NN const U8 *p
 : Used in perly.y
 p      |OP*    |jmaybe         |NN OP *o
 : Used in pp.c 
@@ -1424,6 +1424,7 @@ EMiR      |bool   |_invlist_contains_cp|NN SV* const invlist|const UV cp
 EXpMR  |IV     |_invlist_search        |NN SV* const invlist|const UV cp
 EXMpR  |SV*    |_get_swash_invlist|NN SV* const swash
 EXMpR  |HV*    |_swash_inversion_hash  |NN SV* const swash
+: Not used currently: Mp       |void   |_invlist_dump  |NN SV* const invlist|NN const char * const header
 #endif
 Ap     |void   |taint_env
 Ap     |void   |taint_proper   |NULLOK const char* f|NN const char *const s
@@ -2219,7 +2220,7 @@ sn        |NV|mulexp10    |NV value|I32 exponent
 #if defined(PERL_IN_UTF8_C)
 sRn    |STRLEN |is_utf8_char_slow|NN const U8 *s|const STRLEN len
 sRM    |UV     |check_locale_boundary_crossing|NN const U8* const p|const UV result|NN U8* const ustrp|NN STRLEN *lenp
-sR     |bool   |is_utf8_common |NN const U8 *const p|NN SV **swash|NN const char * const swashname
+iR     |bool   |is_utf8_common |NN const U8 *const p|NN SV **swash|NN const char * const swashname
 sR     |SV*    |swatch_get     |NN SV* swash|UV start|UV span
 #endif
 
diff --git a/embed.h b/embed.h
index 8842e64..e2c2990 100644 (file)
--- a/embed.h
+++ b/embed.h
@@ -27,6 +27,7 @@
 /* Hide global symbols */
 
 #define Gv_AMupdate(a,b)       Perl_Gv_AMupdate(aTHX_ a,b)
+#define _is_utf8__perl_idstart(a)      Perl__is_utf8__perl_idstart(aTHX_ a)
 #define _to_uni_fold_flags(a,b,c,d)    Perl__to_uni_fold_flags(aTHX_ a,b,c,d)
 #define _to_utf8_fold_flags(a,b,c,d,e) Perl__to_utf8_fold_flags(aTHX_ a,b,c,d,e)
 #define _to_utf8_lower_flags(a,b,c,d,e)        Perl__to_utf8_lower_flags(aTHX_ a,b,c,d,e)
 #define do_spawn_nowait(a)     Perl_do_spawn_nowait(aTHX_ a)
 #endif
 #if defined(PERL_CORE) || defined(PERL_EXT)
-#define _is_utf8__perl_idstart(a)      Perl__is_utf8__perl_idstart(aTHX_ a)
 #define av_reify(a)            Perl_av_reify(aTHX_ a)
 #define current_re_engine()    Perl_current_re_engine(aTHX)
-#define is_utf8_X_extend(a)    Perl_is_utf8_X_extend(aTHX_ a)
-#define is_utf8_X_regular_begin(a)     Perl_is_utf8_X_regular_begin(aTHX_ a)
 #define op_clear(a)            Perl_op_clear(aTHX_ a)
 #define qerror(a)              Perl_qerror(aTHX_ a)
 #define reg_named_buff(a,b,c,d)        Perl_reg_named_buff(aTHX_ a,b,c,d)
 #  endif
 #  if defined(PERL_IN_UTF8_C) || defined(PERL_IN_REGCOMP_C) || defined(PERL_IN_REGEXEC_C)
 #define _to_fold_latin1(a,b,c,d)       Perl__to_fold_latin1(aTHX_ a,b,c,d)
+#define is_utf8_X_extend(a)    Perl_is_utf8_X_extend(aTHX_ a)
+#define is_utf8_X_regular_begin(a)     Perl_is_utf8_X_regular_begin(aTHX_ a)
 #  endif
 #  if defined(PERL_OLD_COPY_ON_WRITE)
 #define sv_setsv_cow(a,b)      Perl_sv_setsv_cow(aTHX_ a,b)
index 71c9cc1..68ec99c 100644 (file)
@@ -5,7 +5,7 @@ use strict;
 use warnings;
 use Carp;
 
-our $VERSION = '0.45';
+our $VERSION = '0.46';
 
 require XSLoader;
 
index 8d5b312..2afbcd0 100644 (file)
@@ -3585,6 +3585,20 @@ test_isBLANK_utf8(unsigned char * p)
         RETVAL
 
 bool
+test_isVERTWS_uni(UV ord)
+    CODE:
+        RETVAL = isVERTWS_uni(ord);
+    OUTPUT:
+        RETVAL
+
+bool
+test_isVERTWS_utf8(unsigned char * p)
+    CODE:
+        RETVAL = isVERTWS_utf8(p);
+    OUTPUT:
+        RETVAL
+
+bool
 test_isUPPER_uni(UV ord)
     CODE:
         RETVAL = isUPPER_uni(ord);
index 5ae0eca..9433409 100644 (file)
@@ -5,390 +5,129 @@ use Test::More;
 
 use XS::APItest;
 
-our @blank = (
-0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, # 00 - 0F = C0 controls
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 10 - 1F = C0 controls
-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 20 - 2F = SP - /
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 30 - 3F = 0 - ?
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 40 - 4F = @ - O
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 50 - 5F = P - _
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 60 - 6F = ` - o
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 70 - 7F = p - DEL
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 80 - 8F = C1 controls
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 90 - 9F = C1 controls
-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # A0 - AF = NBSP - MACRON
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # B0 - BF = DEGREE - INVERTED ?
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # C0 - CF = A/GRAVE - I/DIARESIS
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # D0 - DF = ETH - SHARP S
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # E0 - EF = a/GRAVE - i/DIARESIS
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # F0 - FF = eth - y/DIARESIS
-);
-
-our @upper = (
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 00 - 0F = C0 controls
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 10 - 1F = C0 controls
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 20 - 2F = SP - /
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 30 - 3F = 0 - ?
-0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 40 - 4F = @ - O
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, # 50 - 5F = P - _
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 60 - 6F = ` - o
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 70 - 7F = p - DEL
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 80 - 8F = C1 controls
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 90 - 9F = C1 controls
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # A0 - AF = NBSP - MACRON
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # B0 - BF = DEGREE - INVERTED ?
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # C0 - CF = A/GRAVE - I/DIARESIS
-1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, # D0 - DF = ETH - SHARP S
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # E0 - EF = a/GRAVE - i/DIARESIS
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # F0 - FF = eth - y/DIARESIS
-);
-
-our @lower = (
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 00 - 0F = C0 controls
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 10 - 1F = C0 controls
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 20 - 2F = SP - /
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 30 - 3F = 0 - ?
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 40 - 4F = @ - O
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 50 - 5F = P - _
-0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 60 - 6F = ` - o
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, # 70 - 7F = p - DEL
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 80 - 8F = C1 controls
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 90 - 9F = C1 controls
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, # A0 - AF = NBSP - MACRON
-0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, # B0 - BF = DEGREE - INVERTED ?
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # C0 - CF = A/GRAVE - I/DIARESIS
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, # D0 - DF = ETH - SHARP S
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # E0 - EF = a/GRAVE - i/DIARESIS
-1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, # F0 - FF = eth - y/DIARESIS
-);
-
-our @alpha = (
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 00 - 0F = C0 controls
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 10 - 1F = C0 controls
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 20 - 2F = SP - /
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 30 - 3F = 0 - ?
-0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 40 - 4F = @ - O
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, # 50 - 5F = P - _
-0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 60 - 6F = ` - o
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, # 70 - 7F = p - DEL
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 80 - 8F = C1 controls
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 90 - 9F = C1 controls
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, # A0 - AF = NBSP - MACRON
-0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, # B0 - BF = DEGREE - INVERTED ?
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # C0 - CF = A/GRAVE - I/DIARESIS
-1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, # D0 - DF = ETH - SHARP S
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # E0 - EF = a/GRAVE - i/DIARESIS
-1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, # F0 - FF = eth - y/DIARESIS
-);
-
-our @digit = (
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 00 - 0F = C0 controls
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 10 - 1F = C0 controls
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 20 - 2F = SP - /
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, # 30 - 3F = 0 - ?
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 40 - 4F = @ - O
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 50 - 5F = P - _
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 60 - 6F = ` - o
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 70 - 7F = p - DEL
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 80 - 8F = C1 controls
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 90 - 9F = C1 controls
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # A0 - AF = NBSP - MACRON
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # B0 - BF = DEGREE - INVERTED ?
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # C0 - CF = A/GRAVE - I/DIARESIS
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # D0 - DF = ETH - SHARP S
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # E0 - EF = a/GRAVE - i/DIARESIS
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # F0 - FF = eth - y/DIARESIS
-);
-
-our @alnumc = (
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 00 - 0F = C0 controls
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 10 - 1F = C0 controls
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 20 - 2F = SP - /
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, # 30 - 3F = 0 - ?
-0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 40 - 4F = @ - O
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, # 50 - 5F = P - _
-0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 60 - 6F = ` - o
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, # 70 - 7F = p - DEL
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 80 - 8F = C1 controls
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 90 - 9F = C1 controls
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, # A0 - AF = NBSP - MACRON
-0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, # B0 - BF = DEGREE - INVERTED ?
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # C0 - CF = A/GRAVE - I/DIARESIS
-1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, # D0 - DF = ETH - SHARP S
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # E0 - EF = a/GRAVE - i/DIARESIS
-1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, # F0 - FF = eth - y/DIARESIS
-);
-
-our @alnum = (  # Really is \w
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 00 - 0F = C0 controls
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 10 - 1F = C0 controls
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 20 - 2F = SP - /
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, # 30 - 3F = 0 - ?
-0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 40 - 4F = @ - O
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, # 50 - 5F = P - _
-0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 60 - 6F = ` - o
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, # 70 - 7F = p - DEL
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 80 - 8F = C1 controls
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 90 - 9F = C1 controls
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, # A0 - AF = NBSP - MACRON
-0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, # B0 - BF = DEGREE - INVERTED ?
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # C0 - CF = A/GRAVE - I/DIARESIS
-1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, # D0 - DF = ETH - SHARP S
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # E0 - EF = a/GRAVE - i/DIARESIS
-1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, # F0 - FF = eth - y/DIARESIS
-);
-
-our @idfirst = (
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 00 - 0F = C0 controls
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 10 - 1F = C0 controls
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 20 - 2F = SP - /
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 30 - 3F = 0 - ?
-0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 40 - 4F = @ - O
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, # 50 - 5F = P - _
-0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 60 - 6F = ` - o
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, # 70 - 7F = p - DEL
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 80 - 8F = C1 controls
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 90 - 9F = C1 controls
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, # A0 - AF = NBSP - MACRON
-0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, # B0 - BF = DEGREE - INVERTED ?
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # C0 - CF = A/GRAVE - I/DIARESIS
-1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, # D0 - DF = ETH - SHARP S
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # E0 - EF = a/GRAVE - i/DIARESIS
-1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, # F0 - FF = eth - y/DIARESIS
-);
-
-our @space = ( # includes VT for now
-0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, # 00 - 0F = C0 controls
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 10 - 1F = C0 controls
-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 20 - 2F = SP - /
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 30 - 3F = 0 - ?
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 40 - 4F = @ - O
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 50 - 5F = P - _
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 60 - 6F = ` - o
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 70 - 7F = p - DEL
-0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 80 - 8F = C1 controls
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 90 - 9F = C1 controls
-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # A0 - AF = NBSP - MACRON
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # B0 - BF = DEGREE - INVERTED ?
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # C0 - CF = A/GRAVE - I/DIARESIS
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # D0 - DF = ETH - SHARP S
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # E0 - EF = a/GRAVE - i/DIARESIS
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # F0 - FF = eth - y/DIARESIS
-);
-
-our @psxspc = (
-0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, # 00 - 0F = C0 controls
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 10 - 1F = C0 controls
-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 20 - 2F = SP - /
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 30 - 3F = 0 - ?
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 40 - 4F = @ - O
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 50 - 5F = P - _
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 60 - 6F = ` - o
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 70 - 7F = p - DEL
-0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 80 - 8F = C1 controls
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 90 - 9F = C1 controls
-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # A0 - AF = NBSP - MACRON
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # B0 - BF = DEGREE - INVERTED ?
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # C0 - CF = A/GRAVE - I/DIARESIS
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # D0 - DF = ETH - SHARP S
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # E0 - EF = a/GRAVE - i/DIARESIS
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # F0 - FF = eth - y/DIARESIS
-);
-
-our @ascii = (
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 00 - 0F = C0 controls
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 10 - 1F = C0 controls
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 20 - 2F = SP - /
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 30 - 3F = 0 - ?
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 40 - 4F = @ - O
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 50 - 5F = P - _
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 60 - 6F = ` - o
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 70 - 7F = p - DEL
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 80 - 8F = C1 controls
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 90 - 9F = C1 controls
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # A0 - AF = NBSP - MACRON
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # B0 - BF = DEGREE - INVERTED ?
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # C0 - CF = A/GRAVE - I/DIARESIS
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # D0 - DF = ETH - SHARP S
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # E0 - EF = a/GRAVE - i/DIARESIS
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # F0 - FF = eth - y/DIARESIS
-);
-
-our @cntrl = (
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 00 - 0F = C0 controls
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 10 - 1F = C0 controls
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 20 - 2F = SP - /
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 30 - 3F = 0 - ?
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 40 - 4F = @ - O
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 50 - 5F = P - _
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 60 - 6F = ` - o
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, # 70 - 7F = p - DEL
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 80 - 8F = C1 controls
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 90 - 9F = C1 controls
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # A0 - AF = NBSP - MACRON
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # B0 - BF = DEGREE - INVERTED ?
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # C0 - CF = A/GRAVE - I/DIARESIS
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # D0 - DF = ETH - SHARP S
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # E0 - EF = a/GRAVE - i/DIARESIS
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # F0 - FF = eth - y/DIARESIS
-);
-
-our @graph = (
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 00 - 0F = C0 controls
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 10 - 1F = C0 controls
-0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 20 - 2F = SP - /
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 30 - 3F = 0 - ?
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 40 - 4F = @ - O
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 50 - 5F = P - _
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 60 - 6F = ` - o
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, # 70 - 7F = p - DEL
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 80 - 8F = C1 controls
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 90 - 9F = C1 controls
-0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # A0 - AF = NBSP - MACRON
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # B0 - BF = DEGREE - INVERTED ?
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # C0 - CF = A/GRAVE - I/DIARESIS
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # D0 - DF = ETH - SHARP S
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # E0 - EF = a/GRAVE - i/DIARESIS
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # F0 - FF = eth - y/DIARESIS
-);
-
-our @print = (
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 00 - 0F = C0 controls
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 10 - 1F = C0 controls
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 20 - 2F = SP - /
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 30 - 3F = 0 - ?
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 40 - 4F = @ - O
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 50 - 5F = P - _
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 60 - 6F = ` - o
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, # 70 - 7F = p - DEL
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 80 - 8F = C1 controls
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 90 - 9F = C1 controls
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # A0 - AF = NBSP - MACRON
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # B0 - BF = DEGREE - INVERTED ?
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # C0 - CF = A/GRAVE - I/DIARESIS
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # D0 - DF = ETH - SHARP S
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # E0 - EF = a/GRAVE - i/DIARESIS
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # F0 - FF = eth - y/DIARESIS
-);
-
-our @punct = (
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 00 - 0F = C0 controls
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 10 - 1F = C0 controls
-0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 20 - 2F = SP - /
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, # 30 - 3F = 0 - ?
-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 40 - 4F = @ - O
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, # 50 - 5F = P - _
-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 60 - 6F = ` - o
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, # 70 - 7F = p - DEL
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 80 - 8F = C1 controls
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 90 - 9F = C1 controls
-0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, # A0 - AF = NBSP - MACRON
-0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, # B0 - BF = DEGREE - INVERTED ?
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # C0 - CF = A/GRAVE - I/DIARESIS
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # D0 - DF = ETH - SHARP S
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # E0 - EF = a/GRAVE - i/DIARESIS
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # F0 - FF = eth - y/DIARESIS
-);
-
-our @xdigit = (
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 00 - 0F = C0 controls
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 10 - 1F = C0 controls
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 20 - 2F = SP - /
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, # 30 - 3F = 0 - ?
-0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 40 - 4F = @ - O
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 50 - 5F = P - _
-0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 60 - 6F = ` - o
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 70 - 7F = p - DEL
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 80 - 8F = C1 controls
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 90 - 9F = C1 controls
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # A0 - AF = NBSP - MACRON
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # B0 - BF = DEGREE - INVERTED ?
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # C0 - CF = A/GRAVE - I/DIARESIS
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # D0 - DF = ETH - SHARP S
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # E0 - EF = a/GRAVE - i/DIARESIS
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # F0 - FF = eth - y/DIARESIS
-);
-
-our @quotemeta = ( # Certainly isn't a public API member, but tested here
-                    # anyway
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 00 - 0F = C0 controls
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 10 - 1F = C0 controls
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 20 - 2F = SP - /
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, # 30 - 3F = 0 - ?
-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 40 - 4F = @ - O
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, # 50 - 5F = P - _
-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 60 - 6F = ` - o
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, # 70 - 7F = p - DEL
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 80 - 8F = C1 controls
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 90 - 9F = C1 controls
-1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, # A0 - AF = NBSP - MACRON
-1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, # B0 - BF = DEGREE - INVERTED ?
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # C0 - CF = A/GRAVE - I/DIARESIS
-0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, # D0 - DF = ETH - SHARP S
-0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # E0 - EF = a/GRAVE - i/DIARESIS
-0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, # F0 - FF = eth - y/DIARESIS
-);
+use Unicode::UCD qw(prop_invlist);
 
 sub truth($) {  # Converts values so is() works
-    return (shift) ? 1 : "";
+    return (shift) ? 1 : 0;
 }
 
+my %properties = (
+                   # name => Lookup-property name
+                   alnum => 'Word',
+                   alnumc => 'Alnum',
+                   alpha => 'Alpha',
+                   ascii => 'ASCII',
+                   blank => 'Blank',
+                   cntrl => 'Control',
+                   digit => 'Digit',
+                   graph => 'Graph',
+                   idfirst => '_Perl_IDStart',
+                   lower => 'Lower',
+                   print => 'Print',
+                   psxspc => 'XPosixSpace',
+                   punct => 'XPosixPunct',
+                   quotemeta => '_Perl_Quotemeta',
+                   space => 'XPerlSpace',
+                   vertws => 'VertSpace',
+                   upper => 'Upper',
+                   xdigit => 'XDigit',
+                );
+
 my @warnings;
 local $SIG{__WARN__} = sub { push @warnings, @_ };
 
 use charnames ();
-for my $i (0 .. 255, 0x110000) {
-    foreach my $name (qw(   alnum
-                            alnumc
-                            alpha
-                            ascii
-                            blank
-                            cntrl
-                            digit
-                            graph
-                            idfirst
-                            lower
-                            print
-                            psxspc
-                            punct
-                            quotemeta
-                            space
-                            upper
-                            xdigit
-                        )
-    ) {
+foreach my $name (sort keys %properties) {
+    my $property = $properties{$name};
+    my @invlist = prop_invlist($property, '_perl_core_internal_ok');
+    if (! @invlist) {
+        fail("No inversion list found for $property");
+        next;
+    }
+
+    # Include all the Latin1 code points, plus 0x100.
+    my @code_points = (0 .. 256);
+
+    # Then include the next few boundaries above those from this property
+    my $above_latins = 0;
+    foreach my $range_start (@invlist) {
+        next if $range_start < 257;
+        push @code_points, $range_start - 1, $range_start;
+        $above_latins++;
+        last if $above_latins > 5;
+    }
+
+    # And finally one non-Unicode code point.
+    push @code_points, 0x110000;    # Above Unicode, no prop should match
+
+    for my $i (@code_points) {
         my $function = uc($name);
-        no strict 'refs';
-        my $array = *$name{ARRAY};
-        use strict 'refs';
 
-        my $matches = ($i > 0x10FFFF) ? "" : truth($array->[$i]);
+        my $matches = Unicode::UCD::_search_invlist(\@invlist, $i);
+        if (! defined $matches) {
+            $matches = 0;
+        }
+        else {
+            $matches = truth(! ($matches % 2));
+        }
 
-        my $char_name = charnames::viacode($i) // "Above Unicode";
+        my $ret;
+        my $char_name = charnames::viacode($i) // "No name";
         my $display_name = sprintf "\\N{U+%02X, %s}", $i, $char_name;
-        if ($name eq 'quotemeta') {
-            is(eval "test_is${function}($i)", $matches, "is${function}( $display_name )");
+
+        if ($name eq 'quotemeta') { # There is only one macro for this, and is
+                                    # defined only for Latin1 range
+            $ret = truth eval "test_is${function}($i)";
+            if ($@) {
+                fail $@;
+            }
+            else {
+                my $truth = truth($matches && $i < 256);
+                is ($ret, $truth, "is${function}( $display_name ) == $truth");
+            }
             next;
         }
-        is(eval "test_is${function}_A($i)", ($matches && $i < 128), "is${function}_A( $display_name )");
-        is(eval "test_is${function}_L1($i)", $matches, "is${function}_L1( $display_name )");
+        if ($name ne 'vertws') {
+            $ret = truth eval "test_is${function}_A($i)";
+            if ($@) {
+                fail($@);
+            }
+            else {
+                my $truth = truth($matches && $i < 128);
+                is ($ret, $truth, "is${function}_A( $display_name ) == $truth");
+            }
+            $ret = truth eval "test_is${function}_L1($i)";
+            if ($@) {
+                fail($@);
+            }
+            else {
+                my $truth = truth($matches && $i < 256);
+                is ($ret, $truth, "is${function}_L1( $display_name ) == $truth");
+            }
+        }
         next if $name eq 'alnumc';
 
-        is(eval "test_is${function}_uni($i)", $matches, "is${function}_uni( $display_name )");
+        $ret = truth eval "test_is${function}_uni($i)";
+        if ($@) {
+            fail($@);
+        }
+        else {
+            is ($ret, $matches, "is${function}_uni( $display_name ) == $matches");
+        }
 
         my $char = chr($i);
         utf8::upgrade($char);
         $char = quotemeta $char if $char eq '\\' || $char eq "'";
-        is(eval "test_is${function}_utf8('$char')", $matches, "is${function}_utf8( $display_name )");
+        $ret = truth eval "test_is${function}_utf8('$char')";
+        if ($@) {
+            fail($@);
+        }
+        else {
+            is ($ret, $matches, "is${function}_utf8( $display_name ) == $matches");
+        }
     }
 }
 
-ok(test_isBLANK_uni(ord("\N{EM SPACE}")), "EM SPACE is blank in isBLANK_uni()");
-ok(test_isBLANK_utf8("\N{EM SPACE}"), "EM SPACE is blank in isBLANK_utf8()");
-
-ok(! test_isBLANK_uni(ord("\N{GREEK DASIA}")), "GREEK DASIA is not a blank in isBLANK_uni()");
-ok(! test_isBLANK_utf8("\N{GREEK DASIA}"), "GREEK DASIA is not a blank in isBLANK_utf8()");
-
 # This is primarily to make sure that no non-Unicode warnings get generated
 is(scalar @warnings, 0, "No warnings were generated " . join ", ", @warnings);
 
diff --git a/handy.h b/handy.h
index 6b9bca1..178d975 100644 (file)
--- a/handy.h
+++ b/handy.h
@@ -602,11 +602,11 @@ patched there.  The file as of this writing is cpan/Devel-PPPort/parts/inc/misc
 /* ASCII range only */
 #ifdef H_PERL       /* If have access to perl.h, lookup in its table */
 
-/* Character class numbers.  These are used in PL_charclass[] and the ones
- * up through the one that corresponds to <_HIGHEST_REGCOMP_DOT_H_SYNC> are
- * used by regcomp.h.  These use names used in l1_char_class_tab.h but their
- * actual definitions are here.  If that has a name not used here, it won't
- * compile. */
+/* Character class numbers.  For internal core Perl use only.  These are used
+ * in PL_charclass[] and the ones up through the one that corresponds to
+ * <_HIGHEST_REGCOMP_DOT_H_SYNC> are used by regcomp.h.  These use names used
+ * in l1_char_class_tab.h but their actual definitions are here.  If that has a
+ * name not used here, it won't compile. */
 #  define _CC_WORDCHAR           0
 #  define _CC_SPACE              1
 #  define _CC_DIGIT              2
@@ -622,15 +622,16 @@ patched there.  The file as of this writing is cpan/Devel-PPPort/parts/inc/misc
 #  define _CC_XDIGIT            12
 #  define _CC_PSXSPC            13
 #  define _CC_BLANK             14
-#  define _HIGHEST_REGCOMP_DOT_H_SYNC _CC_BLANK
-
-#  define _CC_IDFIRST           15
-#  define _CC_CHARNAME_CONT     16
-#  define _CC_NONLATIN1_FOLD    17
-#  define _CC_QUOTEMETA         18
-#  define _CC_NON_FINAL_FOLD    19
-#  define _CC_IS_IN_SOME_FOLD   20
-/* Unused: 21-31
+#  define _CC_VERTSPACE         15
+#  define _HIGHEST_REGCOMP_DOT_H_SYNC _CC_VERTSPACE
+
+#  define _CC_IDFIRST           16
+#  define _CC_CHARNAME_CONT     17
+#  define _CC_NONLATIN1_FOLD    18
+#  define _CC_QUOTEMETA         19
+#  define _CC_NON_FINAL_FOLD    20
+#  define _CC_IS_IN_SOME_FOLD   21
+/* Unused: 22-31
  * If more bits are needed, one could add a second word for non-64bit
  * QUAD_IS_INT systems, using some #ifdefs to distinguish between having a 2nd
  * word or not.  The IS_IN_SOME_FOLD bit is the most easily expendable, as it
@@ -728,8 +729,10 @@ EXTCONST U32 PL_charclass[];
 #   define isALNUMC_L1(c) _generic_isCC(c, _CC_ALNUMC)
 #   define isALPHA_L1(c)  _generic_isCC(c, _CC_ALPHA)
 #   define isBLANK_L1(c)  _generic_isCC(c, _CC_BLANK)
+
 /*  continuation character for legal NAME in \N{NAME} */
 #   define isCHARNAME_CONT(c) _generic_isCC(c, _CC_CHARNAME_CONT)
+
 #   define isCNTRL_L1(c)  _generic_isCC(c, _CC_CNTRL)
 #   define isGRAPH_L1(c)  _generic_isCC(c, _CC_GRAPH)
 #   define isLOWER_L1(c)  _generic_isCC(c, _CC_LOWER)
@@ -910,35 +913,40 @@ EXTCONST U32 PL_charclass[];
 
 #define isPSXSPC_LC(c)         (isSPACE_LC(c) || (c) == '\v')
 
-/* For use in the macros just below.  If the input is Latin1, use the Latin1
- * (_L1) version of the macro; otherwise use the function.  Won't compile if
- * 'c' isn't unsigned, as won't match function prototype. The macros do bounds
- * checking, so have duplicate checks here, so could create versions of the
- * macros that don't, but experiments show that gcc optimizes them out anyway.
- */
-#define generic_uni(macro, function, c) ((c) < 256               \
-                                         ? CAT2(macro, _L1)(c)   \
-                                         : function(c))
+/* For internal core Perl use only.  If the input is Latin1, use the Latin1
+ * macro; otherwise use the function.  Won't compile if 'c' isn't unsigned, as
+ * won't match function prototype. The macros do bounds checking, so have
+ * duplicate checks here, so could create versions of the macros that don't,
+ * but experiments show that gcc optimizes them out anyway. */
+
 /* Note that all ignore 'use bytes' */
+#define _generic_uni(classnum, function, c) ((c) < 256                    \
+                                             ? _generic_isCC(c, classnum) \
+                                             : function(c))
+
+#define isWORDCHAR_uni(c)       _generic_uni(_CC_WORDCHAR, is_uni_alnum, c)
+#define isALNUM_uni(c)          isWORDCHAR_uni(c)
+#define isBLANK_uni(c)          _generic_uni(_CC_BLANK, is_HORIZWS_cp_high, c)
+#define isIDFIRST_uni(c)        _generic_uni(_CC_IDFIRST, is_uni_idfirst, c)
+#define isALPHA_uni(c)          _generic_uni(_CC_ALPHA, is_uni_alpha, c)
+#define isSPACE_uni(c)          _generic_uni(_CC_SPACE, is_XPERLSPACE_cp_high, c)
+#define isVERTWS_uni(c)         _generic_uni(_CC_VERTSPACE, is_VERTWS_cp_high, c)
+#define isDIGIT_uni(c)          _generic_uni(_CC_DIGIT, is_uni_digit, c)
+#define isUPPER_uni(c)          _generic_uni(_CC_UPPER, is_uni_upper, c)
+#define isLOWER_uni(c)          _generic_uni(_CC_LOWER, is_uni_lower, c)
+#define isASCII_uni(c)          isASCII(c)
 
-#define isALNUM_uni(c)         generic_uni(isWORDCHAR, is_uni_alnum, c)
-#define isBLANK_uni(c)         generic_uni(isBLANK, is_uni_blank, c)
-#define isIDFIRST_uni(c)        generic_uni(isIDFIRST, is_uni_idfirst, c)
-#define isALPHA_uni(c)         generic_uni(isALPHA, is_uni_alpha, c)
-#define isSPACE_uni(c)         generic_uni(isSPACE, is_uni_space, c)
-#define isDIGIT_uni(c)         generic_uni(isDIGIT, is_uni_digit, c)
-#define isUPPER_uni(c)         generic_uni(isUPPER, is_uni_upper, c)
-#define isLOWER_uni(c)         generic_uni(isLOWER, is_uni_lower, c)
-#define isASCII_uni(c)         isASCII(c)
 /* All controls are in Latin1 */
-#define isCNTRL_uni(c)         isCNTRL_L1(c)
-#define isGRAPH_uni(c)         generic_uni(isGRAPH, is_uni_graph, c)
-#define isPRINT_uni(c)         generic_uni(isPRINT, is_uni_print, c)
-#define isPUNCT_uni(c)         generic_uni(isPUNCT, is_uni_punct, c)
-#define isXDIGIT_uni(c)                generic_uni(isXDIGIT, is_uni_xdigit, c)
+#define isCNTRL_uni(c)          isCNTRL_L1(c)
+
+#define isGRAPH_uni(c)          _generic_uni(_CC_GRAPH, is_uni_graph, c)
+#define isPRINT_uni(c)          _generic_uni(_CC_PRINT, is_uni_print, c)
+#define isPUNCT_uni(c)          _generic_uni(_CC_PUNCT, is_uni_punct, c)
+#define isXDIGIT_uni(c)         _generic_uni(_CC_XDIGIT, is_XDIGIT_cp_high, c)
 
 /* Posix and regular space differ only in U+000B, which is in Latin1 */
-#define isPSXSPC_uni(c)                ((c) < 256 ? isPSXSPC_L1(c) : isSPACE_uni(c))
+#define isPSXSPC_uni(c)         _generic_uni(_CC_PSXSPC,                \
+                                             is_XPERLSPACE_cp_high, c)
 
 #define toUPPER_uni(c,s,l)     to_uni_upper(c,s,l)
 #define toTITLE_uni(c,s,l)     to_uni_title(c,s,l)
@@ -960,23 +968,27 @@ EXTCONST U32 PL_charclass[];
 #define isPSXSPC_LC_uni(c)     (isSPACE_LC_uni(c) ||(c) == '\f')
 #define isBLANK_LC_uni(c)      isBLANK(c) /* could be wrong */
 
-/* For use in the macros just below.  If the input is ASCII, use the ASCII (_A)
- * version of the macro; if the input is in the upper Latin1 range, use the
- * Latin1 (_L1) version of the macro, after converting from utf8; otherwise use
- * the function.  This relies on the fact that ASCII characters have the same
- * representation whether utf8 or not */
-#define generic_utf8(macro, function, p) (isASCII(*(p))                        \
-                                         ? CAT2(CAT2(macro,_),A)(*(p))         \
-                                         : (UTF8_IS_DOWNGRADEABLE_START(*(p))) \
-                                           ? CAT2(macro, _L1)                  \
-                                             (TWO_BYTE_UTF8_TO_UNI(*(p),       \
-                                                                   *((p)+1)))  \
-                                           : function(p))
-
-/* Note that all assume that the utf8 has been validated, and ignore 'use
+/* For internal core Perl use only.  If the input is in the Latin1 range, use
+ * the Latin1 macro 'classnum' on 'p' which is a pointer to a UTF-8 string.
+ * Otherwise use the value given by the 'utf8' parameter.  This relies on the
+ * fact that ASCII characters have the same representation whether utf8 or not.
+ * Note that it assumes that the utf8 has been validated, and ignores 'use
  * bytes' */
+#define _generic_utf8_utf8(classnum, p, utf8) (UTF8_IS_INVARIANT(*(p))         \
+                                         ? _generic_isCC(*(p), classnum)       \
+                                         : (UTF8_IS_DOWNGRADEABLE_START(*(p))) \
+                                           ? _generic_isCC(                    \
+                                                   TWO_BYTE_UTF8_TO_UNI(*(p),  \
+                                                                   *((p)+1 )), \
+                                                   classnum)                   \
+                                           : utf8)
+/* Like the above, but calls 'function(p)' to get the utf8 value */
+#define _generic_utf8(classnum, function, p)  \
+                                    _generic_utf8_utf8(classnum, p, function(p))
+
+#define isWORDCHAR_utf8(p)      _generic_utf8(_CC_WORDCHAR, is_utf8_alnum, p)
+#define isALNUM_utf8(p)         isWORDCHAR_utf8(p)  /* back compat */
 
-#define isALNUM_utf8(p)                generic_utf8(isWORDCHAR, is_utf8_alnum, p)
 /* To prevent S_scan_word in toke.c from hanging, we have to make sure that
  * IDFIRST is an alnum.  See
  * http://rt.perl.org/rt3/Ticket/Display.html?id=74022 for more detail than you
@@ -984,38 +996,34 @@ EXTCONST U32 PL_charclass[];
  * isIDFIRST_uni() which it hasn't so far.  (In the ASCII range, there isn't a
  * difference.) This used to be not the XID version, but we decided to go with
  * the more modern Unicode definition */
-#define isIDFIRST_utf8(p)       (isASCII(*(p))                                  \
-                                ? isIDFIRST_A(*(p))                             \
-                                : (UTF8_IS_DOWNGRADEABLE_START(*(p)))           \
-                                  ? isIDFIRST_L1(TWO_BYTE_UTF8_TO_UNI(*(p),     \
-                                                                      *((p)+1)))\
-                                  : Perl__is_utf8__perl_idstart(aTHX_ p))
-#define isIDCONT_utf8(p)       generic_utf8(isWORDCHAR, is_utf8_xidcont, p)
-#define isALPHA_utf8(p)                generic_utf8(isALPHA, is_utf8_alpha, p)
-#define isBLANK_utf8(p)                generic_utf8(isBLANK, is_utf8_blank, p)
-#define isSPACE_utf8(p)                generic_utf8(isSPACE, is_utf8_space, p)
-#define isDIGIT_utf8(p)                generic_utf8(isDIGIT, is_utf8_digit, p)
-#define isUPPER_utf8(p)                generic_utf8(isUPPER, is_utf8_upper, p)
-#define isLOWER_utf8(p)                generic_utf8(isLOWER, is_utf8_lower, p)
+#define isIDFIRST_utf8(p)       _generic_utf8(_CC_IDFIRST,               \
+                                              _is_utf8__perl_idstart, p)
+
+#define isIDCONT_utf8(p)        _generic_utf8(_CC_WORDCHAR, is_utf8_xidcont, p)
+#define isALPHA_utf8(p)         _generic_utf8(_CC_ALPHA, is_utf8_alpha, p)
+#define isBLANK_utf8(p)         _generic_utf8(_CC_BLANK, is_HORIZWS_high, p)
+#define isSPACE_utf8(p)         _generic_utf8(_CC_SPACE, is_XPERLSPACE_high, p)
+#define isVERTWS_utf8(p)        _generic_utf8(_CC_VERTSPACE, is_VERTWS_high, p)
+#define isDIGIT_utf8(p)         _generic_utf8(_CC_DIGIT, is_utf8_digit, p)
+#define isUPPER_utf8(p)         _generic_utf8(_CC_UPPER, is_utf8_upper, p)
+#define isLOWER_utf8(p)         _generic_utf8(_CC_LOWER, is_utf8_lower, p)
+
 /* Because ASCII is invariant under utf8, the non-utf8 macro works */
-#define isASCII_utf8(p)                isASCII(*p)
-#define isCNTRL_utf8(p)                generic_utf8(isCNTRL, is_utf8_cntrl, p)
-#define isGRAPH_utf8(p)                generic_utf8(isGRAPH, is_utf8_graph, p)
-#define isPRINT_utf8(p)                generic_utf8(isPRINT, is_utf8_print, p)
-#define isPUNCT_utf8(p)                generic_utf8(isPUNCT, is_utf8_punct, p)
-#define isXDIGIT_utf8(p)       generic_utf8(isXDIGIT, is_utf8_xdigit, p)
+#define isASCII_utf8(p)         isASCII(*p)
+
+#define isCNTRL_utf8(p)         _generic_utf8_utf8(_CC_CNTRL, p, 0)
+#define isGRAPH_utf8(p)         _generic_utf8(_CC_GRAPH, is_utf8_graph, p)
+#define isPRINT_utf8(p)         _generic_utf8(_CC_PRINT, is_utf8_print, p)
+#define isPUNCT_utf8(p)         _generic_utf8(_CC_PUNCT, is_utf8_punct, p)
+#define isXDIGIT_utf8(p)        _generic_utf8(_CC_XDIGIT, is_XDIGIT_high, p)
 #define toUPPER_utf8(p,s,l)    to_utf8_upper(p,s,l)
 #define toTITLE_utf8(p,s,l)    to_utf8_title(p,s,l)
 #define toLOWER_utf8(p,s,l)    to_utf8_lower(p,s,l)
 
 /* Posix and regular space differ only in U+000B, which is in ASCII (and hence
  * Latin1 */
-#define isPSXSPC_utf8(p)       ((isASCII(*(p)))                               \
-                                ? isPSXSPC_A(*(p))                             \
-                                : (UTF8_IS_DOWNGRADEABLE_START(*(p))           \
-                                 ? isPSXSPC_L1(TWO_BYTE_UTF8_TO_UNI(*(p),     \
-                                                                     *((p)+1)))\
-                                  : isSPACE_utf8(p)))
+#define isPSXSPC_utf8(p)        _generic_utf8(_CC_PSXSPC, is_XPERLSPACE_high, p)
+
 #define isALNUM_LC_utf8(p)     isALNUM_LC_uvchr(valid_utf8_to_uvchr(p,  0))
 #define isIDFIRST_LC_utf8(p)   isIDFIRST_LC_uvchr(valid_utf8_to_uvchr(p,  0))
 #define isALPHA_LC_utf8(p)     isALPHA_LC_uvchr(valid_utf8_to_uvchr(p,  0))
@@ -1033,12 +1041,14 @@ EXTCONST U32 PL_charclass[];
 #define isPSXSPC_LC_utf8(c)    (isSPACE_LC_utf8(c) ||(c) == '\f')
 
 /* This conversion works both ways, strangely enough. On EBCDIC platforms,
- * CTRL-@ is 0, CTRL-A is 1, etc, just like on ASCII */
+ * CTRL-@ is 0, CTRL-A is 1, etc, just like on ASCII, except that they don't
+ * necessarily mean the same characters, e.g. CTRL-D is 4 on both systems, but
+ * that is EOT on ASCII;  ST on EBCDIC */
 #  define toCTRL(c)    (toUPPER(NATIVE_TO_UNI(c)) ^ 64)
 
 /* Line numbers are unsigned, 32 bits. */
 typedef U32 line_t;
-#define NOLINE ((line_t) 4294967295UL)
+#define NOLINE ((line_t) 4294967295UL)  /* = FFFFFFFF */
 
 /* Helpful alias for version prescan */
 #define is_LAX_VERSION(a,b) \
index d346993..ba6df16 100644 (file)
 /* U+07 BEL */ (1U<<_CC_ASCII)|(1U<<_CC_CNTRL)|(1U<<_CC_QUOTEMETA),
 /* U+08 BS */ (1U<<_CC_ASCII)|(1U<<_CC_CNTRL)|(1U<<_CC_QUOTEMETA),
 /* U+09 HT */ (1U<<_CC_ASCII)|(1U<<_CC_BLANK)|(1U<<_CC_CNTRL)|(1U<<_CC_PSXSPC)|(1U<<_CC_QUOTEMETA)|(1U<<_CC_SPACE),
-/* U+0A LF */ (1U<<_CC_ASCII)|(1U<<_CC_CNTRL)|(1U<<_CC_PSXSPC)|(1U<<_CC_QUOTEMETA)|(1U<<_CC_SPACE),
-/* U+0B VT */ (1U<<_CC_ASCII)|(1U<<_CC_CNTRL)|(1U<<_CC_PSXSPC)|(1U<<_CC_QUOTEMETA)|(1U<<_CC_SPACE),
-/* U+0C FF */ (1U<<_CC_ASCII)|(1U<<_CC_CNTRL)|(1U<<_CC_PSXSPC)|(1U<<_CC_QUOTEMETA)|(1U<<_CC_SPACE),
-/* U+0D CR */ (1U<<_CC_ASCII)|(1U<<_CC_CNTRL)|(1U<<_CC_PSXSPC)|(1U<<_CC_QUOTEMETA)|(1U<<_CC_SPACE),
+/* U+0A LF */ (1U<<_CC_ASCII)|(1U<<_CC_CNTRL)|(1U<<_CC_PSXSPC)|(1U<<_CC_QUOTEMETA)|(1U<<_CC_SPACE)|(1U<<_CC_VERTSPACE),
+/* U+0B VT */ (1U<<_CC_ASCII)|(1U<<_CC_CNTRL)|(1U<<_CC_PSXSPC)|(1U<<_CC_QUOTEMETA)|(1U<<_CC_SPACE)|(1U<<_CC_VERTSPACE),
+/* U+0C FF */ (1U<<_CC_ASCII)|(1U<<_CC_CNTRL)|(1U<<_CC_PSXSPC)|(1U<<_CC_QUOTEMETA)|(1U<<_CC_SPACE)|(1U<<_CC_VERTSPACE),
+/* U+0D CR */ (1U<<_CC_ASCII)|(1U<<_CC_CNTRL)|(1U<<_CC_PSXSPC)|(1U<<_CC_QUOTEMETA)|(1U<<_CC_SPACE)|(1U<<_CC_VERTSPACE),
 /* U+0E SO */ (1U<<_CC_ASCII)|(1U<<_CC_CNTRL)|(1U<<_CC_QUOTEMETA),
 /* U+0F SI */ (1U<<_CC_ASCII)|(1U<<_CC_CNTRL)|(1U<<_CC_QUOTEMETA),
 /* U+10 DLE */ (1U<<_CC_ASCII)|(1U<<_CC_CNTRL)|(1U<<_CC_QUOTEMETA),
 /* U+82 BPH */ (1U<<_CC_CNTRL)|(1U<<_CC_QUOTEMETA),
 /* U+83 NBH */ (1U<<_CC_CNTRL)|(1U<<_CC_QUOTEMETA),
 /* U+84 IND */ (1U<<_CC_CNTRL)|(1U<<_CC_QUOTEMETA),
-/* U+85 NEL */ (1U<<_CC_CNTRL)|(1U<<_CC_PSXSPC)|(1U<<_CC_QUOTEMETA)|(1U<<_CC_SPACE),
+/* U+85 NEL */ (1U<<_CC_CNTRL)|(1U<<_CC_PSXSPC)|(1U<<_CC_QUOTEMETA)|(1U<<_CC_SPACE)|(1U<<_CC_VERTSPACE),
 /* U+86 SSA */ (1U<<_CC_CNTRL)|(1U<<_CC_QUOTEMETA),
 /* U+87 ESA */ (1U<<_CC_CNTRL)|(1U<<_CC_QUOTEMETA),
 /* U+88 HTS */ (1U<<_CC_CNTRL)|(1U<<_CC_QUOTEMETA),
index a882ab5..02023a7 100644 (file)
@@ -5,7 +5,7 @@ use warnings;
 no warnings 'surrogate';    # surrogates can be inputs to this
 use charnames ();
 
-our $VERSION = '0.46';
+our $VERSION = '0.47';
 
 require Exporter;
 
@@ -2255,7 +2255,8 @@ sub prop_invlist ($;$) {
 
 sub _search_invlist {
     # Find the range in the inversion list which contains a code point; that
-    # is, find i such that l[i] <= code_point < l[i+1]
+    # is, find i such that l[i] <= code_point < l[i+1].  Returns undef if no
+    # such i.
 
     # If this is ever made public, could use to speed up .t specials.  Would
     # need to use code point argument, as in other functions in this pm
@@ -2265,7 +2266,10 @@ sub _search_invlist {
     # Verify non-neg numeric  XXX
 
     my $max_element = @$list_ref - 1;
-    return if ! $max_element < 0;     # Undef if list is empty.
+
+    # Return undef if list is empty or requested item is before the first element.
+    return if $max_element < 0;
+    return if $code_point < $list_ref->[0];
 
     # Short cut something at the far-end of the table.  This also allows us to
     # refer to element [$i+1] without fear of being out-of-bounds in the loop
diff --git a/proto.h b/proto.h
index d574a6d..b044718 100644 (file)
--- a/proto.h
+++ b/proto.h
@@ -1778,18 +1778,6 @@ PERL_CALLCONV bool       Perl_is_uni_xdigit_lc(pTHX_ UV c)
                        __attribute__warn_unused_result__
                        __attribute__pure__;
 
-PERL_CALLCONV bool     Perl_is_utf8_X_extend(pTHX_ const U8 *p)
-                       __attribute__warn_unused_result__
-                       __attribute__nonnull__(pTHX_1);
-#define PERL_ARGS_ASSERT_IS_UTF8_X_EXTEND      \
-       assert(p)
-
-PERL_CALLCONV bool     Perl_is_utf8_X_regular_begin(pTHX_ const U8 *p)
-                       __attribute__warn_unused_result__
-                       __attribute__nonnull__(pTHX_1);
-#define PERL_ARGS_ASSERT_IS_UTF8_X_REGULAR_BEGIN       \
-       assert(p)
-
 PERL_CALLCONV bool     Perl_is_utf8_alnum(pTHX_ const U8 *p)
                        __attribute__warn_unused_result__
                        __attribute__nonnull__(pTHX_1);
@@ -7258,7 +7246,7 @@ STATIC STRLEN     S_is_utf8_char_slow(const U8 *s, const STRLEN len)
 #define PERL_ARGS_ASSERT_IS_UTF8_CHAR_SLOW     \
        assert(s)
 
-STATIC bool    S_is_utf8_common(pTHX_ const U8 *const p, SV **swash, const char * const swashname)
+PERL_STATIC_INLINE bool        S_is_utf8_common(pTHX_ const U8 *const p, SV **swash, const char * const swashname)
                        __attribute__warn_unused_result__
                        __attribute__nonnull__(pTHX_1)
                        __attribute__nonnull__(pTHX_2)
@@ -7291,6 +7279,18 @@ PERL_CALLCONV UV Perl__to_fold_latin1(pTHX_ const U8 c, U8 *p, STRLEN *lenp, con
 #define PERL_ARGS_ASSERT__TO_FOLD_LATIN1       \
        assert(p); assert(lenp)
 
+PERL_CALLCONV bool     Perl_is_utf8_X_extend(pTHX_ const U8 *p)
+                       __attribute__warn_unused_result__
+                       __attribute__nonnull__(pTHX_1);
+#define PERL_ARGS_ASSERT_IS_UTF8_X_EXTEND      \
+       assert(p)
+
+PERL_CALLCONV bool     Perl_is_utf8_X_regular_begin(pTHX_ const U8 *p)
+                       __attribute__warn_unused_result__
+                       __attribute__nonnull__(pTHX_1);
+#define PERL_ARGS_ASSERT_IS_UTF8_X_REGULAR_BEGIN       \
+       assert(p)
+
 #endif
 #if defined(PERL_IN_UTIL_C)
 STATIC bool    S_ckwarn_common(pTHX_ U32 w);
index f7b4229..64e4453 100644 (file)
 : 0 )
 
 /*** GENERATED CODE ***/
+#define is_HORIZWS_high(s)                                                  \
+( ( 0xE1 == ((U8*)s)[0] ) ?                                                 \
+    ( ( 0x9A == ((U8*)s)[1] ) ?                                             \
+       ( ( 0x80 == ((U8*)s)[2] ) ? 3 : 0 )                                 \
+    : ( ( 0xA0 == ((U8*)s)[1] ) && ( 0x8E == ((U8*)s)[2] ) ) ? 3 : 0 )      \
+: ( 0xE2 == ((U8*)s)[0] ) ?                                                 \
+    ( ( 0x80 == ((U8*)s)[1] ) ?                                             \
+       ( ( ( ((U8*)s)[2] <= 0x8A ) || 0xAF == ((U8*)s)[2] ) ? 3 : 0 )      \
+    : ( ( 0x81 == ((U8*)s)[1] ) && ( 0x9F == ((U8*)s)[2] ) ) ? 3 : 0 )      \
+: ( ( ( 0xE3 == ((U8*)s)[0] ) && ( 0x80 == ((U8*)s)[1] ) ) && ( 0x80 == ((U8*)s)[2] ) ) ? 3 : 0 )
+
+/*** GENERATED CODE ***/
+#define is_HORIZWS_high_safe(s,e)                                           \
+( ((e)-(s) > 2) ?                                                           \
+    ( ( 0xE1 == ((U8*)s)[0] ) ?                                             \
+       ( ( 0x9A == ((U8*)s)[1] ) ?                                         \
+           ( ( 0x80 == ((U8*)s)[2] ) ? 3 : 0 )                             \
+       : ( ( 0xA0 == ((U8*)s)[1] ) && ( 0x8E == ((U8*)s)[2] ) ) ? 3 : 0 )  \
+    : ( 0xE2 == ((U8*)s)[0] ) ?                                             \
+       ( ( 0x80 == ((U8*)s)[1] ) ?                                         \
+           ( ( ( 0x80 <= ((U8*)s)[2] && ((U8*)s)[2] <= 0x8A ) || 0xAF == ((U8*)s)[2] ) ? 3 : 0 )\
+       : ( ( 0x81 == ((U8*)s)[1] ) && ( 0x9F == ((U8*)s)[2] ) ) ? 3 : 0 )  \
+    : ( ( ( 0xE3 == ((U8*)s)[0] ) && ( 0x80 == ((U8*)s)[1] ) ) && ( 0x80 == ((U8*)s)[2] ) ) ? 3 : 0 )\
+: 0 )
+
+/*** GENERATED CODE ***/
 #define is_HORIZWS_cp(cp)                                                   \
 ( 0x09 == cp || ( 0x09 < cp &&                                              \
 ( 0x20 == cp || ( 0x20 < cp &&                                              \
 ( 0x202F == cp || ( 0x202F < cp &&                                          \
 ( 0x205F == cp || 0x3000 == cp ) ) ) ) ) ) ) ) ) ) ) ) ) ) )
 
+/*** GENERATED CODE ***/
+#define is_HORIZWS_cp_high(cp)                                              \
+( 0x1680 == cp || ( 0x1680 < cp &&                                          \
+( 0x180E == cp || ( 0x180E < cp &&                                          \
+( ( 0x2000 <= cp && cp <= 0x200A ) || ( 0x200A < cp &&                      \
+( 0x202F == cp || ( 0x202F < cp &&                                          \
+( 0x205F == cp || 0x3000 == cp ) ) ) ) ) ) ) ) )
+
 /*
        VERTWS: Vertical Whitespace: \v \V
 
 : 0 )
 
 /*** GENERATED CODE ***/
+#define is_VERTWS_high(s)                                                   \
+( ( ( ( 0xE2 == ((U8*)s)[0] ) && ( 0x80 == ((U8*)s)[1] ) ) && ( ( ((U8*)s)[2] & 0xFE ) == 0xA8 ) ) ? 3 : 0 )
+
+/*** GENERATED CODE ***/
+#define is_VERTWS_high_safe(s,e)                                            \
+( ( ( ( ((e)-(s) > 2) && ( 0xE2 == ((U8*)s)[0] ) ) && ( 0x80 == ((U8*)s)[1] ) ) && ( ( ((U8*)s)[2] & 0xFE ) == 0xA8 ) ) ? 3 : 0 )
+
+/*** GENERATED CODE ***/
 #define is_VERTWS_latin1(s)                                                 \
 ( ( 0x0A <= ((U8*)s)[0] && ((U8*)s)[0] <= 0x0D ) || 0x85 == ((U8*)s)[0] )
 
 ( 0x85 == cp || ( 0x85 < cp &&                                              \
 ( 0x2028 == cp || 0x2029 == cp ) ) ) ) )
 
+/*** GENERATED CODE ***/
+#define is_VERTWS_cp_high(cp)                                               \
+( 0x2028 == cp || 0x2029 == cp )
+
+/*
+       XDIGIT: Hexadecimal digits
+
+       \p{XDigit}
+*/
+/*** GENERATED CODE ***/
+#define is_XDIGIT_utf8(s)                                                   \
+( ( ( 0x30 <= ((U8*)s)[0] && ((U8*)s)[0] <= 0x39 ) || ( 0x41 <= ((U8*)s)[0] && ((U8*)s)[0] <= 0x46 ) || ( 0x61 <= ((U8*)s)[0] && ((U8*)s)[0] <= 0x66 ) ) ? 1\
+: ( 0xEF == ((U8*)s)[0] ) ?                                                 \
+    ( ( 0xBC == ((U8*)s)[1] ) ?                                             \
+       ( ( ( 0x90 <= ((U8*)s)[2] && ((U8*)s)[2] <= 0x99 ) || ( 0xA1 <= ((U8*)s)[2] && ((U8*)s)[2] <= 0xA6 ) ) ? 3 : 0 )\
+    : ( ( 0xBD == ((U8*)s)[1] ) && ( 0x81 <= ((U8*)s)[2] && ((U8*)s)[2] <= 0x86 ) ) ? 3 : 0 )\
+: 0 )
+
+/*** GENERATED CODE ***/
+#define is_XDIGIT_high(s)                                                   \
+( ( 0xEF == ((U8*)s)[0] ) ?                                                 \
+    ( ( 0xBC == ((U8*)s)[1] ) ?                                             \
+       ( ( ( 0x90 <= ((U8*)s)[2] && ((U8*)s)[2] <= 0x99 ) || ( 0xA1 <= ((U8*)s)[2] && ((U8*)s)[2] <= 0xA6 ) ) ? 3 : 0 )\
+    : ( ( 0xBD == ((U8*)s)[1] ) && ( 0x81 <= ((U8*)s)[2] && ((U8*)s)[2] <= 0x86 ) ) ? 3 : 0 )\
+: 0 )
+
+/*** GENERATED CODE ***/
+#define is_XDIGIT_cp_high(cp)                                               \
+( ( 0xFF10 <= cp && cp <= 0xFF19 ) || ( 0xFF19 < cp &&                      \
+( ( 0xFF21 <= cp && cp <= 0xFF26 ) || ( 0xFF41 <= cp && cp <= 0xFF46 ) ) ) )
+
+/*
+       XPERLSPACE: \p{XPerlSpace}
+
+       \p{XPerlSpace}
+*/
+/*** GENERATED CODE ***/
+#define is_XPERLSPACE(s,is_utf8)                                            \
+( ( ( 0x09 <= ((U8*)s)[0] && ((U8*)s)[0] <= 0x0D ) || 0x20 == ((U8*)s)[0] ) ? 1\
+: ( is_utf8 ) ?                                                             \
+    ( ( 0xC2 == ((U8*)s)[0] ) ?                                             \
+       ( ( 0x85 == ((U8*)s)[1] || 0xA0 == ((U8*)s)[1] ) ? 2 : 0 )          \
+    : ( 0xE1 == ((U8*)s)[0] ) ?                                             \
+       ( ( 0x9A == ((U8*)s)[1] ) ?                                         \
+           ( ( 0x80 == ((U8*)s)[2] ) ? 3 : 0 )                             \
+       : ( ( 0xA0 == ((U8*)s)[1] ) && ( 0x8E == ((U8*)s)[2] ) ) ? 3 : 0 )  \
+    : ( 0xE2 == ((U8*)s)[0] ) ?                                             \
+       ( ( 0x80 == ((U8*)s)[1] ) ?                                         \
+           ( ( ( 0x80 <= ((U8*)s)[2] && ((U8*)s)[2] <= 0x8A ) || ( ((U8*)s)[2] & 0xFE ) == 0xA8 || 0xAF == ((U8*)s)[2] ) ? 3 : 0 )\
+       : ( ( 0x81 == ((U8*)s)[1] ) && ( 0x9F == ((U8*)s)[2] ) ) ? 3 : 0 )  \
+    : ( ( ( 0xE3 == ((U8*)s)[0] ) && ( 0x80 == ((U8*)s)[1] ) ) && ( 0x80 == ((U8*)s)[2] ) ) ? 3 : 0 )\
+: ( 0x85 == ((U8*)s)[0] || 0xA0 == ((U8*)s)[0] ) )
+
+/*** GENERATED CODE ***/
+#define is_XPERLSPACE_utf8(s)                                               \
+( ( ( 0x09 <= ((U8*)s)[0] && ((U8*)s)[0] <= 0x0D ) || 0x20 == ((U8*)s)[0] ) ? 1\
+: ( 0xC2 == ((U8*)s)[0] ) ?                                                 \
+    ( ( 0x85 == ((U8*)s)[1] || 0xA0 == ((U8*)s)[1] ) ? 2 : 0 )              \
+: ( 0xE1 == ((U8*)s)[0] ) ?                                                 \
+    ( ( 0x9A == ((U8*)s)[1] ) ?                                             \
+       ( ( 0x80 == ((U8*)s)[2] ) ? 3 : 0 )                                 \
+    : ( ( 0xA0 == ((U8*)s)[1] ) && ( 0x8E == ((U8*)s)[2] ) ) ? 3 : 0 )      \
+: ( 0xE2 == ((U8*)s)[0] ) ?                                                 \
+    ( ( 0x80 == ((U8*)s)[1] ) ?                                             \
+       ( ( ( ((U8*)s)[2] <= 0x8A ) || ( ((U8*)s)[2] & 0xFE ) == 0xA8 || 0xAF == ((U8*)s)[2] ) ? 3 : 0 )\
+    : ( ( 0x81 == ((U8*)s)[1] ) && ( 0x9F == ((U8*)s)[2] ) ) ? 3 : 0 )      \
+: ( ( ( 0xE3 == ((U8*)s)[0] ) && ( 0x80 == ((U8*)s)[1] ) ) && ( 0x80 == ((U8*)s)[2] ) ) ? 3 : 0 )
+
+/*** GENERATED CODE ***/
+#define is_XPERLSPACE_high(s)                                               \
+( ( 0xE1 == ((U8*)s)[0] ) ?                                                 \
+    ( ( 0x9A == ((U8*)s)[1] ) ?                                             \
+       ( ( 0x80 == ((U8*)s)[2] ) ? 3 : 0 )                                 \
+    : ( ( 0xA0 == ((U8*)s)[1] ) && ( 0x8E == ((U8*)s)[2] ) ) ? 3 : 0 )      \
+: ( 0xE2 == ((U8*)s)[0] ) ?                                                 \
+    ( ( 0x80 == ((U8*)s)[1] ) ?                                             \
+       ( ( ( ((U8*)s)[2] <= 0x8A ) || ( ((U8*)s)[2] & 0xFE ) == 0xA8 || 0xAF == ((U8*)s)[2] ) ? 3 : 0 )\
+    : ( ( 0x81 == ((U8*)s)[1] ) && ( 0x9F == ((U8*)s)[2] ) ) ? 3 : 0 )      \
+: ( ( ( 0xE3 == ((U8*)s)[0] ) && ( 0x80 == ((U8*)s)[1] ) ) && ( 0x80 == ((U8*)s)[2] ) ) ? 3 : 0 )
+
+/*** GENERATED CODE ***/
+#define is_XPERLSPACE_cp_high(cp)                                           \
+( 0x1680 == cp || ( 0x1680 < cp &&                                          \
+( 0x180E == cp || ( 0x180E < cp &&                                          \
+( ( 0x2000 <= cp && cp <= 0x200A ) || ( 0x200A < cp &&                      \
+( 0x2028 == cp || ( 0x2028 < cp &&                                          \
+( 0x2029 == cp || ( 0x2029 < cp &&                                          \
+( 0x202F == cp || ( 0x202F < cp &&                                          \
+( 0x205F == cp || 0x3000 == cp ) ) ) ) ) ) ) ) ) ) ) ) )
+
 /*
        REPLACEMENT: Unicode REPLACEMENT CHARACTER
 
index 0c62cb0..8b7c84c 100644 (file)
--- a/regcomp.c
+++ b/regcomp.c
@@ -7383,8 +7383,8 @@ Perl__invlist_search(pTHX_ SV* const invlist, const UV cp)
      * And benchmarks show that caching gives better results.  We also test
      * here if the code point is within the bounds of the list.  These tests
      * replace others that would have had to be made anyway to make sure that
-     * the array bounds were not exceeded, and give us extra information at the
-     * same time */
+     * the array bounds were not exceeded, and these give us extra information
+     * at the same time */
     if (cp >= array[mid]) {
         if (cp >= array[highest_element]) {
             return highest_element;
@@ -8252,15 +8252,17 @@ Perl__invlist_contents(pTHX_ SV* const invlist)
 }
 #endif
 
-#if 0
+#ifdef PERL_ARGS_ASSERT__INVLIST_DUMP
 void
-S_invlist_dump(pTHX_ SV* const invlist, const char * const header)
+Perl__invlist_dump(pTHX_ SV* const invlist, const char * const header)
 {
     /* Dumps out the ranges in an inversion list.  The string 'header'
      * if present is output on a line before the first range */
 
     UV start, end;
 
+    PERL_ARGS_ASSERT__INVLIST_DUMP;
+
     if (header && strlen(header)) {
        PerlIO_printf(Perl_debug_log, "%s\n", header);
     }
@@ -8269,8 +8271,12 @@ S_invlist_dump(pTHX_ SV* const invlist, const char * const header)
        if (end == UV_MAX) {
            PerlIO_printf(Perl_debug_log, "0x%04"UVXf" .. INFINITY\n", start);
        }
+       else if (end != start) {
+           PerlIO_printf(Perl_debug_log, "0x%04"UVXf" .. 0x%04"UVXf"\n",
+                                                start,         end);
+       }
        else {
-           PerlIO_printf(Perl_debug_log, "0x%04"UVXf" .. 0x%04"UVXf"\n", start, end);
+           PerlIO_printf(Perl_debug_log, "0x%04"UVXf"\n", start);
        }
     }
 }
@@ -11756,7 +11762,7 @@ parseit:
                    Safefree(name);
                }
                RExC_parse = e + 1;
-               namedclass = ANYOF_MAX;  /* no official name, but it's named */
+               namedclass = ANYOF_UNIPROP;  /* no official name, but it's named */
 
                /* \p means they want Unicode semantics */
                RExC_uni_semantics = 1;
@@ -12154,8 +12160,7 @@ parseit:
                     DO_N_POSIX(ret, namedclass, posixes,
                                             PL_PosixXDigit, PL_XPosixXDigit);
                    break;
-               case ANYOF_MAX:
-                   /* this is to handle \p and \P */
+               case ANYOF_UNIPROP: /* this is to handle \p and \P */
                    break;
                default:
                    vFAIL("Invalid [::] class");
@@ -12498,7 +12503,7 @@ parseit:
                     *flagp |= HASWIDTH|SIMPLE;
                     break;
 
-                case ANYOF_MAX:
+                case ANYOF_UNIPROP:
                     break;
 
                 case ANYOF_NBLANK:
@@ -12580,7 +12585,7 @@ parseit:
 
             ret = reg_node(pRExC_state, op);
 
-            if (PL_regkind[op] == POSIXD) {
+            if (PL_regkind[op] == POSIXD || PL_regkind[op] == NPOSIXD) {
                 if (! SIZE_ONLY) {
                     FLAGS(ret) = arg;
                 }
@@ -14092,7 +14097,7 @@ Perl_regprop(pTHX_ const regexp *prog, SV *sv, const regnode *o)
 
        Perl_sv_catpvf(aTHX_ sv, "%s]", PL_colors[1]);
     }
-    else if (k == POSIXD) {
+    else if (k == POSIXD || k == NPOSIXD) {
         U8 index = FLAGS(o) * 2;
         if (index > (sizeof(anyofs) / sizeof(anyofs[0]))) {
             Perl_sv_catpvf(aTHX_ sv, "[illegal type=%d])", index);
index e9a64fe..1c7f454 100644 (file)
--- a/regcomp.h
+++ b/regcomp.h
@@ -415,18 +415,28 @@ struct regnode_charclass_class {
 #define ANYOF_BLANK    ((_CC_BLANK) * 2)     /* GNU extension: space and tab: non-vertical space */
 #define ANYOF_NBLANK   ((ANYOF_BLANK) + 1)
 
-#define ANYOF_MAX      32
-#if (ANYOF_MAX <= _HIGHEST_REGCOMP_DOT_H_SYNC * 2 + 1)
+#define ANYOF_MAX      ((ANYOF_NBLANK) + 1) /* So upper loop limit is written:
+                                               '< ANYOF_MAX' */
+#if (ANYOF_MAX > 32)                        /* Must fit in 32-bit word */
 #   error Problem with handy.h _CC_foo #defines
 #endif
 
-/* pseudo classes, not stored in the class bitmap, but used as flags
+/* pseudo classes below this, not stored in the class bitmap, but used as flags
    during compilation of char classes */
 
-#define ANYOF_VERTWS   (ANYOF_MAX+1)
-#define ANYOF_NVERTWS  (ANYOF_MAX+2)
-#define ANYOF_HORIZWS  (ANYOF_MAX+3)
-#define ANYOF_NHORIZWS (ANYOF_MAX+4)
+#define ANYOF_VERTWS    ((ANYOF_MAX)+0)
+#define ANYOF_NVERTWS   ((ANYOF_MAX)+1)
+
+#if (ANYOF_VERTWS != (_CC_VERTSPACE) * 2) \
+     || (_CC_VERTSPACE != _HIGHEST_REGCOMP_DOT_H_SYNC)
+#   error Problem with handy.h _CC_VERTSPACE #define
+#endif
+
+#define ANYOF_HORIZWS  ((ANYOF_MAX)+2)
+#define ANYOF_NHORIZWS ((ANYOF_MAX)+3)
+
+#define ANYOF_UNIPROP   ((ANYOF_MAX)+4)  /* Used to indicate a Unicode
+                                            property: \p{} or \P{} */
 
 /* Backward source code compatibility. */
 
index 3b4db9c..eb8ba46 100644 (file)
@@ -89,10 +89,10 @@ POSIXD      POSIXD,     none 0 S   ; currently unused except as a placeholder
 POSIXL      POSIXD,     none 0 S   ; currently unused except as a placeholder
 POSIXU      POSIXD,     none 0 S   ; currently unused except as a placeholder
 POSIXA      POSIXD,     none 0 S   ; Some [[:class:]] under /a; the FLAGS field gives which one
-NPOSIXD     POSIXD,     none 0 S   ; currently unused except as a placeholder
-NPOSIXL     POSIXD,     none 0 S   ; currently unused except as a placeholder
-NPOSIXU     POSIXD,     none 0 S   ; currently unused except as a placeholder
-NPOSIXA     POSIXD,     none 0 S   ; complement of POSIXA, [[:^class:]]
+NPOSIXD     NPOSIXD,    none 0 S   ; currently unused except as a placeholder
+NPOSIXL     NPOSIXD,    none 0 S   ; currently unused except as a placeholder
+NPOSIXU     NPOSIXD,    none 0 S   ; currently unused except as a placeholder
+NPOSIXA     NPOSIXD,    none 0 S   ; complement of POSIXA, [[:^class:]]
 # End of order is important (within groups)
 
 CLUMP       CLUMP,      no 0 V    ; Match any extended grapheme cluster sequence
index 0aac59e..33b7c87 100644 (file)
@@ -42,6 +42,7 @@ my @properties = qw(
     UPPER
     WORDCHAR
     XDIGIT
+    VERTSPACE
     IS_IN_SOME_FOLD
 );
 
@@ -170,8 +171,8 @@ for my $ord (0..255) {
         if (! ($name =~ s/_L1$//)) {
 
             # Here, isn't an _L1.  If its _A, it's automatically false for
-            # non-ascii.  The only one current one (besides ASCII) without a
-            # suffix is valid over the whole range.
+            # non-ascii.  The only current ones (besides ASCII) without a
+            # suffix are valid over the whole range.
             next if $name =~ s/_A$// && $ord >= 128;
 
         }
index 508f687..0bab570 100755 (executable)
@@ -164,10 +164,12 @@ sub __uni_latin1 {
     my $str= shift;
     my $max= 0;
     my @cp;
+    my @cp_high;
     my $only_has_invariants = 1;
     for my $ch ( split //, $str ) {
         my $cp= ord $ch;
         push @cp, $cp;
+        push @cp_high, $cp if $cp > 255;
         $max= $cp if $max < $cp;
         if (! ASCII_PLATFORM && $only_has_invariants) {
             if ($cp > 255) {
@@ -192,7 +194,7 @@ sub __uni_latin1 {
         utf8::upgrade($u);
         $u= [ unpack "U0C*", $u ] if defined $u;
     }
-    return ( \@cp, $n, $l, $u );
+    return ( \@cp, \@cp_high, $n, $l, $u );
 }
 
 #
@@ -376,17 +378,17 @@ sub new {
         } else {
             die "Unparsable line: $txt\n";
         }
-        my ( $cp, $low, $latin1, $utf8 )= __uni_latin1( $str );
+        my ( $cp, $cp_high, $low, $latin1, $utf8 )= __uni_latin1( $str );
         my $UTF8= $low   || $utf8;
         my $LATIN1= $low || $latin1;
         my $high = (scalar grep { $_ < 256 } @$cp) ? 0 : $utf8;
         #die Dumper($txt,$cp,$low,$latin1,$utf8)
         #    if $txt=~/NEL/ or $utf8 and @$utf8>3;
 
-        @{ $self->{strs}{$str} }{qw( str txt low utf8 latin1 high cp UTF8 LATIN1 )}=
-          ( $str, $txt, $low, $utf8, $latin1, $high, $cp, $UTF8, $LATIN1 );
+        @{ $self->{strs}{$str} }{qw( str txt low utf8 latin1 high cp cp_high UTF8 LATIN1 )}=
+          ( $str, $txt, $low, $utf8, $latin1, $high, $cp, $cp_high, $UTF8, $LATIN1 );
         my $rec= $self->{strs}{$str};
-        foreach my $key ( qw(low utf8 latin1 high cp UTF8 LATIN1) ) {
+        foreach my $key ( qw(low utf8 latin1 high cp cp_high UTF8 LATIN1) ) {
             $self->{size}{$key}{ 0 + @{ $self->{strs}{$str}{$key} } }++
               if $self->{strs}{$str}{$key};
         }
@@ -490,7 +492,7 @@ sub _optree {
     return $else if !@conds;
 
 
-    my $test= $test_type eq 'cp' ? "cp" : "((U8*)s)[$depth]";
+    my $test= $test_type =~ /^cp/ ? "cp" : "((U8*)s)[$depth]";
     # first we loop over the possible keys/conditions and find out what they look like
     # we group conditions with the same optree together.
     my %dmp_res;
@@ -540,7 +542,7 @@ sub optree {
     my %opt= @_;
     my $trie= $self->make_trie( $opt{type}, $opt{max_depth} );
     $opt{ret_type} ||= 'len';
-    my $test_type= $opt{type} eq 'cp' ? 'cp' : 'depth';
+    my $test_type= $opt{type} =~ /^cp/ ? 'cp' : 'depth';
     return $self->_optree( $trie, $test_type, $opt{ret_type}, $opt{else}, 0 );
 }
 
@@ -588,7 +590,7 @@ sub length_optree {
     my $type= $opt{type};
 
     die "Can't do a length_optree on type 'cp', makes no sense."
-      if $type eq 'cp';
+      if $type =~ /^cp/;
 
     my ( @size, $method );
 
@@ -1136,7 +1138,7 @@ sub render {
 # make a macro of a given type.
 # calls into make_trie and (generic_|length_)optree as needed
 # Opts are:
-# type     : 'cp','generic','high','low','latin1','utf8','LATIN1','UTF8'
+# type     : 'cp','cp_high', 'generic','high','low','latin1','utf8','LATIN1','UTF8'
 # ret_type : 'cp' or 'len'
 # safe     : add length guards to macro
 #
@@ -1155,9 +1157,9 @@ sub make_macro {
     my %opts= @_;
     my $type= $opts{type} || 'generic';
     die "Can't do a 'cp' on multi-codepoint character class '$self->{op}'"
-      if $type eq 'cp'
+      if $type =~ /^cp/
       and $self->{has_multi};
-    my $ret_type= $opts{ret_type} || ( $opts{type} eq 'cp' ? 'cp' : 'len' );
+    my $ret_type= $opts{ret_type} || ( $opts{type} =~ /^cp/ ? 'cp' : 'len' );
     my $method;
     if ( $opts{safe} ) {
         $method= 'length_optree';
@@ -1167,8 +1169,8 @@ sub make_macro {
         $method= 'optree';
     }
     my $optree= $self->$method( %opts, type => $type, ret_type => $ret_type );
-    my $text= $self->render( $optree, $type eq 'cp', \%opts );
-    my @args= $type eq 'cp' ? 'cp' : 's';
+    my $text= $self->render( $optree, ($type =~ /^cp/) ? 1 : 0, \%opts );
+    my @args= $type =~ /^cp/ ? 'cp' : 's';
     push @args, "e" if $opts{safe};
     push @args, "is_utf8" if $type eq 'generic';
     push @args, "len" if $ret_type eq 'both';
@@ -1228,7 +1230,7 @@ if ( !caller ) {
             my ( $type, $ret )= split /-/, $type_spec;
             $ret ||= 'len';
             foreach my $mod ( @mods ) {
-                next if $mod eq 'safe' and $type eq 'cp';
+                next if $mod eq 'safe' and $type =~ /^cp/;
                 delete $mods{$mod};
                 my $macro= $obj->make_macro(
                     type     => $type,
@@ -1329,6 +1331,10 @@ if ( !caller ) {
 #   cp          generate a macro whose name is 'is_BASE_cp' and defines a
 #               class that returns true if the UV parameter is a member of the
 #               class; false if not.
+#   cp_high     like cp, but it is assumed that it is known that the UV
+#               parameter is above Latin1.  The name of the generated macro is
+#               'is_BASE_cp_high'.  This is different from high-cp, derived
+#               below.
 # A macro of the given type is generated for each type listed in the input.
 # The default return value is the number of octets read to generate the match.
 # Append "-cp" to the type to have it instead return the matched codepoint.
@@ -1383,13 +1389,21 @@ LNBREAK: Line Break: \R
 \p{VertSpace}
 
 HORIZWS: Horizontal Whitespace: \h \H
-=> generic UTF8 LATIN1 cp :fast safe
+=> generic UTF8 LATIN1 high cp cp_high :fast safe
 \p{HorizSpace}
 
 VERTWS: Vertical Whitespace: \v \V
-=> generic UTF8 LATIN1 cp :fast safe
+=> generic UTF8 high LATIN1 cp cp_high :fast safe
 \p{VertSpace}
 
+XDIGIT: Hexadecimal digits
+=> UTF8 high cp_high :fast
+\p{XDigit}
+
+XPERLSPACE: \p{XPerlSpace}
+=> generic UTF8 high cp_high :fast
+\p{XPerlSpace}
+
 REPLACEMENT: Unicode REPLACEMENT CHARACTER
 => UTF8 :safe
 0xFFFD
index 72e2d7b..d0560ce 100644 (file)
--- a/regexec.c
+++ b/regexec.c
@@ -159,11 +159,11 @@ static const char* const non_utf8_target_but_utf8_required
        bool throw_away PERL_UNUSED_DECL; \
        ENTER; save_re_context(); \
        throw_away = CAT2(is_utf8_,class)((const U8*)" "); \
+        PERL_UNUSED_VAR(throw_away); \
        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_GCB()  /* Grapheme cluster boundaries */        \
         /* No asserts are done for some of these, in case called on a   */  \
@@ -1712,16 +1712,14 @@ S_find_byclass(pTHX_ regexp * prog, const regnode *c, char *s,
            );
            break;
        case SPACEU:
-           REXEC_FBC_CSCAN_PRELOAD(
-               LOAD_UTF8_CHARCLASS_SPACE(),
-               *s == ' ' || swash_fetch(PL_utf8_space,(U8*)s, utf8_target),
+           REXEC_FBC_CSCAN(
+               is_XPERLSPACE_utf8(s),
                 isSPACE_L1((U8) *s)
            );
            break;
        case SPACE:
-           REXEC_FBC_CSCAN_PRELOAD(
-               LOAD_UTF8_CHARCLASS_SPACE(),
-               *s == ' ' || swash_fetch(PL_utf8_space,(U8*)s, utf8_target),
+           REXEC_FBC_CSCAN(
+               is_XPERLSPACE_utf8(s),
                 isSPACE((U8) *s)
            );
            break;
@@ -1737,16 +1735,14 @@ S_find_byclass(pTHX_ regexp * prog, const regnode *c, char *s,
            );
            break;
        case NSPACEU:
-           REXEC_FBC_CSCAN_PRELOAD(
-               LOAD_UTF8_CHARCLASS_SPACE(),
-               !( *s == ' ' || swash_fetch(PL_utf8_space,(U8*)s, utf8_target)),
+           REXEC_FBC_CSCAN(
+               ! is_XPERLSPACE_utf8(s),
                 ! isSPACE_L1((U8) *s)
            );
            break;
        case NSPACE:
-           REXEC_FBC_CSCAN_PRELOAD(
-               LOAD_UTF8_CHARCLASS_SPACE(),
-               !(*s == ' ' || swash_fetch(PL_utf8_space,(U8*)s, utf8_target)),
+           REXEC_FBC_CSCAN(
+               ! is_XPERLSPACE_utf8(s),
                 ! isSPACE((U8) *s)
            );
            break;
@@ -4330,11 +4326,73 @@ S_regmatch(pTHX_ regmatch_info *reginfo, char *startpos, regnode *prog)
                  ALNUMA, NALNUMA, isWORDCHAR_A,
                  alnum, "a");
 
-        CCC_TRY_U(SPACE,  NSPACE,  isSPACE,
-                 SPACEL, NSPACEL, isSPACE_LC, isSPACE_LC_utf8,
-                 SPACEU, NSPACEU, isSPACE_L1,
-                 SPACEA, NSPACEA, isSPACE_A,
-                 space, " ");
+        case SPACEL:
+            PL_reg_flags |= RF_tainted;
+            if (NEXTCHR_IS_EOS) {
+                sayNO;
+            }
+            if (utf8_target && UTF8_IS_CONTINUED(nextchr)) {
+                if (! isSPACE_LC_utf8((U8 *) locinput)) {
+                    sayNO;
+                }
+            }
+            else if (! isSPACE_LC((U8) nextchr)) {
+                    sayNO;
+            }
+            goto increment_locinput;
+
+        case NSPACEL:
+            PL_reg_flags |= RF_tainted;
+            if (NEXTCHR_IS_EOS) {
+                sayNO;
+            }
+            if (utf8_target && UTF8_IS_CONTINUED(nextchr)) {
+                if (isSPACE_LC_utf8((U8 *) locinput)) {
+                    sayNO;
+                }
+            }
+            else if (isSPACE_LC(nextchr)) {
+                    sayNO;
+            }
+            goto increment_locinput;
+
+        case SPACE:
+            if (utf8_target) {
+                goto utf8_space;
+            }
+            /* FALL THROUGH */
+        case SPACEA:
+            if (NEXTCHR_IS_EOS || ! isSPACE_A(nextchr)) {
+                sayNO;
+            }
+            /* Matched a utf8-invariant, so don't have to worry about utf8 */
+            locinput++;
+            break;
+
+        case NSPACE:
+            if (utf8_target) {
+                goto utf8_nspace;
+            }
+            /* FALL THROUGH */
+        case NSPACEA:
+            if (NEXTCHR_IS_EOS || isSPACE_A(nextchr)) {
+                sayNO;
+            }
+            goto increment_locinput;
+
+        case SPACEU:
+          utf8_space:
+            if (NEXTCHR_IS_EOS || ! is_XPERLSPACE(locinput, utf8_target)) {
+                sayNO;
+            }
+            goto increment_locinput;
+
+        case NSPACEU:
+          utf8_nspace:
+            if (NEXTCHR_IS_EOS || is_XPERLSPACE(locinput, utf8_target)) {
+                sayNO;
+            }
+            goto increment_locinput;
 
         CCC_TRY(DIGIT,  NDIGIT,  isDIGIT,
                DIGITL, NDIGITL, isDIGIT_LC, isDIGIT_LC_utf8,
@@ -6901,10 +6959,8 @@ S_regrepeat(pTHX_ const regexp *prog, char **startposp, const regnode *p, I32 ma
 
     utf8_space:
 
-           LOAD_UTF8_CHARCLASS_SPACE();
-           while (hardcount < max && scan < loceol &&
-                  (*scan == ' ' ||
-                    swash_fetch(PL_utf8_space,(U8*)scan, utf8_target)))
+           while (hardcount < max && scan < loceol
+                   && is_XPERLSPACE_utf8((U8*)scan))
             {
                scan += UTF8SKIP(scan);
                hardcount++;
@@ -6954,10 +7010,8 @@ S_regrepeat(pTHX_ const regexp *prog, char **startposp, const regnode *p, I32 ma
 
     utf8_Nspace:
 
-           LOAD_UTF8_CHARCLASS_SPACE();
-           while (hardcount < max && scan < loceol &&
-                  ! (*scan == ' ' ||
-                      swash_fetch(PL_utf8_space,(U8*)scan, utf8_target)))
+           while (hardcount < max && scan < loceol
+                   && ! is_XPERLSPACE_utf8((U8*)scan))
             {
                scan += UTF8SKIP(scan);
                hardcount++;
index b8278cc..2024d15 100644 (file)
@@ -229,10 +229,10 @@ EXTCONST U8 PL_regkind[] = {
        POSIXD,         /* POSIXL                 */
        POSIXD,         /* POSIXU                 */
        POSIXD,         /* POSIXA                 */
-       POSIXD,         /* NPOSIXD                */
-       POSIXD,         /* NPOSIXL                */
-       POSIXD,         /* NPOSIXU                */
-       POSIXD,         /* NPOSIXA                */
+       NPOSIXD,        /* NPOSIXD                */
+       NPOSIXD,        /* NPOSIXL                */
+       NPOSIXD,        /* NPOSIXU                */
+       NPOSIXD,        /* NPOSIXA                */
        CLUMP,          /* CLUMP                  */
        BRANCH,         /* BRANCH                 */
        BACK,           /* BACK                   */
diff --git a/utf8.c b/utf8.c
index 829db7d..5621317 100644 (file)
--- a/utf8.c
+++ b/utf8.c
@@ -1515,17 +1515,13 @@ Perl_is_uni_ascii(pTHX_ UV c)
 bool
 Perl_is_uni_blank(pTHX_ UV c)
 {
-    U8 tmpbuf[UTF8_MAXBYTES+1];
-    uvchr_to_utf8(tmpbuf, c);
-    return is_utf8_blank(tmpbuf);
+    return isBLANK_uni(c);
 }
 
 bool
 Perl_is_uni_space(pTHX_ UV c)
 {
-    U8 tmpbuf[UTF8_MAXBYTES+1];
-    uvchr_to_utf8(tmpbuf, c);
-    return is_utf8_space(tmpbuf);
+    return isSPACE_uni(c);
 }
 
 bool
@@ -1585,9 +1581,7 @@ Perl_is_uni_punct(pTHX_ UV c)
 bool
 Perl_is_uni_xdigit(pTHX_ UV c)
 {
-    U8 tmpbuf[UTF8_MAXBYTES_CASE+1];
-    uvchr_to_utf8(tmpbuf, c);
-    return is_utf8_xdigit(tmpbuf);
+    return isXDIGIT_uni(c);
 }
 
 UV
@@ -1930,7 +1924,7 @@ Perl_to_uni_lower_lc(pTHX_ U32 c)
     return (U32)to_uni_lower(c, tmpbuf, &len);
 }
 
-static bool
+PERL_STATIC_INLINE bool
 S_is_utf8_common(pTHX_ const U8 *const p, SV **swash,
                 const char *const swashname)
 {
@@ -2061,7 +2055,7 @@ Perl_is_utf8_blank(pTHX_ const U8 *p)
 
     PERL_ARGS_ASSERT_IS_UTF8_BLANK;
 
-    return is_utf8_common(p, &PL_utf8_blank, "XPosixBlank");
+    return isBLANK_utf8(p);
 }
 
 bool
@@ -2071,7 +2065,7 @@ Perl_is_utf8_space(pTHX_ const U8 *p)
 
     PERL_ARGS_ASSERT_IS_UTF8_SPACE;
 
-    return is_utf8_common(p, &PL_utf8_space, "IsXPerlSpace");
+    return isSPACE_utf8(p);
 }
 
 bool
@@ -2147,15 +2141,7 @@ Perl_is_utf8_cntrl(pTHX_ const U8 *p)
 
     PERL_ARGS_ASSERT_IS_UTF8_CNTRL;
 
-    if (isASCII(*p)) {
-       return isCNTRL_A(*p);
-    }
-
-    /* All controls are in Latin1 */
-    if (! UTF8_IS_DOWNGRADEABLE_START(*p)) {
-       return 0;
-    }
-    return isCNTRL_L1(TWO_BYTE_UTF8_TO_UNI(*p, *(p+1)));
+    return isCNTRL_utf8(p);
 }
 
 bool
@@ -2195,7 +2181,7 @@ Perl_is_utf8_xdigit(pTHX_ const U8 *p)
 
     PERL_ARGS_ASSERT_IS_UTF8_XDIGIT;
 
-    return is_utf8_common(p, &PL_utf8_xdigit, "IsXDigit");
+    return is_XDIGIT_utf8(p);
 }
 
 bool
@@ -2788,8 +2774,8 @@ Perl__core_swash_init(pTHX_ const char* pkg, const char* name, SV *listsv, I32 m
      * by calling utf8_heavy.pl in the general case.  The returned value may be
      * the swash's inversion list instead if the input parameters allow it.
      * Which is returned should be immaterial to callers, as the only
-     * operations permitted on a swash, swash_fetch() and
-     * _get_swash_invlist(), handle both these transparently.
+     * operations permitted on a swash, swash_fetch(), _get_swash_invlist(),
+     * and swash_to_invlist() handle both these transparently.
      *
      * This interface should only be used by functions that won't destroy or
      * adversely change the swash, as doing so affects all other uses of the
@@ -2894,7 +2880,8 @@ Perl__core_swash_init(pTHX_ const char* pkg, const char* name, SV *listsv, I32 m
        SAVEFREESV(errsv_save);
        /* If we already have a pointer to the method, no need to use
         * call_method() to repeat the lookup.  */
-       if (method ? call_sv(MUTABLE_SV(method), G_SCALAR)
+       if (method
+            ? call_sv(MUTABLE_SV(method), G_SCALAR)
            : call_sv(newSVpvs_flags("SWASHNEW", SVs_TEMP), G_SCALAR | G_METHOD))
        {
            retval = *PL_stack_sp--;
@@ -3003,6 +2990,7 @@ Perl__core_swash_init(pTHX_ const char* pkg, const char* name, SV *listsv, I32 m
            else SvREFCNT_inc_simple_void_NN(swash_invlist);
        }
 
+        /* Use the inversion list stand-alone if small enough */
         if ((int) _invlist_len(swash_invlist) <= invlist_swash_boundary) {
            SvREFCNT_dec(retval);
            if (!swash_invlist_unclaimed)
@@ -3661,7 +3649,8 @@ Perl__swash_inversion_hash(pTHX_ SV* const swash)
     STRLEN lcur;
     HV *const hv = MUTABLE_HV(SvRV(swash));
 
-    /* The string containing the main body of the table */
+    /* The string containing the main body of the table.  This will have its
+     * assertion fail if the swash has been converted to its inversion list */
     SV** const listsvp = hv_fetchs(hv, "LIST", FALSE);
 
     SV** const typesvp = hv_fetchs(hv, "TYPE", FALSE);
@@ -3900,22 +3889,36 @@ Perl__swash_to_invlist(pTHX_ SV* const swash)
     HV *const hv = MUTABLE_HV(SvRV(swash));
     UV elements = 0;    /* Number of elements in the inversion list */
     U8 empty[] = "";
+    SV** listsvp;
+    SV** typesvp;
+    SV** bitssvp;
+    SV** extssvp;
+    SV** invert_it_svp;
 
-    /* The string containing the main body of the table */
-    SV** const listsvp = hv_fetchs(hv, "LIST", FALSE);
-    SV** const typesvp = hv_fetchs(hv, "TYPE", FALSE);
-    SV** const bitssvp = hv_fetchs(hv, "BITS", FALSE);
-    SV** const extssvp = hv_fetchs(hv, "EXTRAS", FALSE);
-    SV** const invert_it_svp = hv_fetchs(hv, "INVERT_IT", FALSE);
-
-    const U8* const typestr = (U8*)SvPV_nolen(*typesvp);
-    const STRLEN bits  = SvUV(*bitssvp);
-    const STRLEN octets = bits >> 3; /* if bits == 1, then octets == 0 */
+    U8* typestr;
+    STRLEN bits;
+    STRLEN octets; /* if bits == 1, then octets == 0 */
     U8 *x, *xend;
     STRLEN xcur;
 
     SV* invlist;
 
+    /* If not a hash, it must be the swash's inversion list instead */
+    if (SvTYPE(hv) != SVt_PVHV) {
+        return (SV*) hv;
+    }
+
+    /* The string containing the main body of the table */
+    listsvp = hv_fetchs(hv, "LIST", FALSE);
+    typesvp = hv_fetchs(hv, "TYPE", FALSE);
+    bitssvp = hv_fetchs(hv, "BITS", FALSE);
+    extssvp = hv_fetchs(hv, "EXTRAS", FALSE);
+    invert_it_svp = hv_fetchs(hv, "INVERT_IT", FALSE);
+
+    typestr = (U8*)SvPV_nolen(*typesvp);
+    bits  = SvUV(*bitssvp);
+    octets = bits >> 3; /* if bits == 1, then octets == 0 */
+
     PERL_ARGS_ASSERT__SWASH_TO_INVLIST;
 
     /* read $swash->{LIST} */
@@ -4426,7 +4429,6 @@ Perl_foldEQ_utf8_flags(pTHX_ const char *s1, char **pe1, register UV l1, bool u1
                f1 = (U8 *) p1;
                n1 = UTF8SKIP(f1);
            }
-
            else {
                /* If in locale matching, we use two sets of rules, depending
                 * on if the code point is above or below 255.  Here, we test