This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
reg_fold.t: Allow to work on early Unicodes
[perl5.git] / t / re / reg_fold.t
1 #!perl
2
3 BEGIN {
4     chdir 't' if -d 't';
5     @INC = '../lib';
6     require './test.pl';
7     skip_all_if_miniperl("no dynamic loading on miniperl, no File::Spec");
8 }
9
10 use strict;
11 use warnings;
12 my @tests;
13
14 my $file="../lib/unicore/CaseFolding.txt";
15 my @folds;
16 use Unicode::UCD;
17
18 # Use the Unicode data file if we are on an ASCII platform (which its data is
19 # for), and it is in the modern format (starting in Unicode 3.1.0) and it is
20 # available.  This avoids being affected by potential bugs introduced by other
21 # layers of Perl
22 if (ord('A') == 65
23     && pack "C*", split /\./, Unicode::UCD::UnicodeVersion() ge v3.1.0
24     && open my $fh, "<", $file)
25 {
26     @folds = <$fh>;
27 }
28 else {
29     my ($invlist_ref, $invmap_ref, undef, $default)
30                                     = Unicode::UCD::prop_invmap('Case_Folding');
31     for my $i (0 .. @$invlist_ref - 1 - 1) {
32         next if $invmap_ref->[$i] == $default;
33         my $adjust = -1;
34         for my $j ($invlist_ref->[$i] .. $invlist_ref->[$i+1] -1) {
35             $adjust++;
36
37             # Single-code point maps go to a 'C' type
38             if (! ref $invmap_ref->[$i]) {
39                 push @folds, sprintf("%04X; C; %04X\n",
40                                      $j,
41                                      $invmap_ref->[$i] + $adjust);
42             }
43             else {  # Multi-code point maps go to 'F'.  prop_invmap()
44                     # guarantees that no adjustment is needed for these,
45                     # as the range will contain just one element
46                 push @folds, sprintf("%04X; F; %s\n",
47                                     $j,
48                                     join " ", map { sprintf "%04X", $_ }
49                                                     @{$invmap_ref->[$i]});
50             }
51         }
52     }
53 }
54
55 for  (@folds) {
56     chomp;
57     my ($line,$comment)= split/\s+#\s+/, $_;
58     $comment = "" unless defined $comment;
59     my ($cp,$type,@folded)=split/[\s;]+/,$line||'';
60     next unless $type and ($type eq 'F' or $type eq 'C');
61     my $fold_above_latin1 = grep { hex("0x$_") > 255 } @folded;
62     $_="\\x{$_}" for @folded;
63     my $cpv=hex("0x$cp");
64     my $chr="\\x{$cp}";
65     my @str;
66     foreach my $swap (0, 1) {   # swap lhs and rhs, or not.
67         foreach my $charclass (0, 1) {   # Put rhs in [...], or not
68             my $lhs;
69             my $rhs;
70             if ($swap) {
71                 $lhs = join "", @folded;
72                 $rhs = $chr;
73                 $rhs = "[$rhs]" if $charclass;
74             } else {
75                 #next if $charclass && @folded > 1;
76                 $lhs = $chr;
77                 $rhs = "";
78                 foreach my $rhs_char (@folded) {
79                     $rhs .= '[' if $charclass;
80                     $rhs .=  $rhs_char;
81                     $rhs .= ']' if $charclass;
82                 }
83             }
84             $lhs = "\"$lhs\"";
85             $rhs = "/^$rhs\$/iu";
86
87             # Try both Latin1 and Unicode for code points below 256
88             foreach my $upgrade ("", 'utf8::upgrade($c); ') {
89                 if ($upgrade) { # No need to upgrade if already must be in
90                                 # utf8
91                     next if $swap && $fold_above_latin1;
92                     next if !$swap && $cpv > 255;
93                 }
94                 my $eval = "my \$c = $lhs; $upgrade\$c =~ $rhs";
95                 #print __LINE__, ": $eval\n";
96                 push @tests, qq[ok(eval '$eval', '$eval - $comment')];
97                 if (! $swap && $charclass && @folded > 1)
98                 {
99                     $tests[-1]="TODO: { local \$::TODO='A multi-char fold \"foo\", doesnt work for /[f][o][o]/i';\n$tests[-1] }"
100                 }
101             }
102         }
103     }
104 }
105
106 # Now verify the case folding tables.  First compute the mappings without
107 # resorting to the functions we're testing.
108
109 # Initialize the array so each $i maps to itself.
110 my @fold_ascii;
111 for my $i (0 .. 255) {
112     $fold_ascii[$i] = $i;
113 }
114 my @fold_latin1 = @fold_ascii;
115
116 # Override the uppercase elements to fold to their lower case equivalents,
117 # using the fact that 'A' in ASCII is 0x41, 'a' is 0x41+32, 'B' is 0x42, and
118 # so on.  The same paradigm applies for most of the Latin1 range cased
119 # characters, but in posix anything outside ASCII maps to itself, as we've
120 # already set up.
121 for my $i (0x41 .. 0x5A, 0xC0 .. 0xD6, 0xD8 .. 0xDE) {
122     my $upper_ord = ord_latin1_to_native($i);
123     my $lower_ord = ord_latin1_to_native($i + 32);
124
125     $fold_latin1[$upper_ord] = $lower_ord;
126
127     next if $i > 127;
128     $fold_ascii[$upper_ord] = $lower_ord;
129 }
130
131 # Same for folding lower to the upper equivalents
132 for my $i (0x61 .. 0x7A, 0xE0 .. 0xF6, 0xF8 .. 0xFE) {
133     my $lower_ord = ord_latin1_to_native($i);
134     my $upper_ord = ord_latin1_to_native($i - 32);
135
136     $fold_latin1[$lower_ord] = $upper_ord;
137
138     next if $i > 127;
139     $fold_ascii[$lower_ord] = $upper_ord;
140 }
141
142 # Test every latin1 character for the correct values in both /u and /d
143 for my $i (0 .. 255) {
144     my $chr = sprintf "\\x%02X", $i;
145     my $hex_fold_ascii = sprintf "0x%02X", $fold_ascii[$i];
146     my $hex_fold_latin1 = sprintf "0x%02X", $fold_latin1[$i];
147     push @tests, qq[like chr($hex_fold_ascii), qr/(?d:$chr)/i, 'chr($hex_fold_ascii) =~ qr/(?d:$chr)/i'];
148     push @tests, qq[like chr($hex_fold_latin1), qr/(?u:$chr)/i, 'chr($hex_fold_latin1) =~ qr/(?u:$chr)/i'];
149 }
150
151
152 push @tests, qq[like chr(0x0430), qr/[=\x{0410}-\x{0411}]/i, 'Bug #71752 Unicode /i char in a range'];
153 push @tests, qq[like 'a', qr/\\p{Upper}/i, "'a' =~ /\\\\p{Upper}/i"];
154 push @tests, q[my $c = "\x{212A}"; my $p = qr/(?:^[\x{004B}_]+$)/i; utf8::upgrade($p); like $c, $p, 'Bug #78994: my $c = "\x{212A}"; my $p = qr/(?:^[\x{004B}_]+$)/i; utf8::upgrade($p); $c =~ $p'];
155
156 use charnames ":full";
157 push @tests, q[my $re1 = "\N{WHITE SMILING FACE}";like "\xE8", qr/[\w$re1]/, 'my $re = "\N{WHITE SMILING FACE}"; "\xE8" =~ qr/[\w$re]/'];
158 push @tests, q[my $re2 = "\N{WHITE SMILING FACE}";like "\xE8", qr/\w|$re2/, 'my $re = "\N{WHITE SMILING FACE}"; "\xE8" =~ qr/\w|$re/'];
159
160 eval join ";\n","plan tests=>". (scalar @tests), @tests, "1"
161     or die $@;
162 __DATA__