191f110e0Safresh1package regcharclass_multi_char_folds; 291f110e0Safresh1use 5.015; 391f110e0Safresh1use strict; 491f110e0Safresh1use warnings; 591f110e0Safresh1use Unicode::UCD "prop_invmap"; 691f110e0Safresh1 791f110e0Safresh1# This returns an array of strings of the form 891f110e0Safresh1# "\x{foo}\x{bar}\x{baz}" 991f110e0Safresh1# of the sequences of code points that are multi-character folds in the 1091f110e0Safresh1# current Unicode version. If the parameter is 1, all such folds are 1191f110e0Safresh1# returned. If the parameters is 0, only the ones containing exclusively 1291f110e0Safresh1# Latin1 characters are returned. In the latter case all combinations of 1391f110e0Safresh1# Latin1 characters that can fold to the base one are returned. Thus for 1491f110e0Safresh1# 'ss', it would return in addition, 'Ss', 'sS', and 'SS'. This is because 1591f110e0Safresh1# this code is designed to help regcomp.c, and EXACTFish regnodes. For 1656d68f1eSafresh1# non-UTF-8 patterns, the strings are not necessarily folded, so we need to 1756d68f1eSafresh1# check for the upper and lower case versions. For UTF-8 patterns, the 1856d68f1eSafresh1# strings are folded, except in EXACTFL nodes) so we only need to worry about 1956d68f1eSafresh1# the fold version. All folded-to characters in non-UTF-8 (Latin1) are 2056d68f1eSafresh1# members of fold-pairs, at least within Latin1, 'k', and 'K', for example. 2156d68f1eSafresh1# So there aren't complications with dealing with unfolded input. That's not 2256d68f1eSafresh1# true of UTF-8 patterns, where things can get tricky. Thus for EXACTFL nodes 2356d68f1eSafresh1# where things aren't all folded, code has to be written specially to handle 2456d68f1eSafresh1# this, instead of the macros here being extended to try to handle it. 25b8851fccSafresh1# 26b8851fccSafresh1# There are no non-ASCII Latin1 multi-char folds currently, and none likely to 27b8851fccSafresh1# be ever added. Thus the output is the same as if it were just asking for 28b8851fccSafresh1# ASCII characters, not full Latin1. Hence, it is suitable for generating 2956d68f1eSafresh1# things that match EXACTFAA. It does check for and croak if there ever were 30b8851fccSafresh1# to be an upper Latin1 range multi-character fold. 3191f110e0Safresh1# 3291f110e0Safresh1# This is designed for input to regen/regcharlass.pl. 3391f110e0Safresh1 3491f110e0Safresh1sub gen_combinations ($;) { 3591f110e0Safresh1 # Generate all combinations for the first parameter which is an array of 3691f110e0Safresh1 # arrays. 3791f110e0Safresh1 3891f110e0Safresh1 my ($fold_ref, $string, $i) = @_; 3991f110e0Safresh1 $string = "" unless $string; 4091f110e0Safresh1 $i = 0 unless $i; 4191f110e0Safresh1 4291f110e0Safresh1 my @ret; 4391f110e0Safresh1 4491f110e0Safresh1 # Look at each element in this level's array. 45*eac174f2Safresh1 if (ref $fold_ref->[$i]) { 4691f110e0Safresh1 foreach my $j (0 .. @{$fold_ref->[$i]} - 1) { 4791f110e0Safresh1 4891f110e0Safresh1 # Append its representation to what we have currently 4956d68f1eSafresh1 my $new_string = $fold_ref->[$i][$j] =~ /[[:print:]]/ 5056d68f1eSafresh1 ? ($string . chr $fold_ref->[$i][$j]) 5156d68f1eSafresh1 : sprintf "$string\\x{%X}", $fold_ref->[$i][$j]; 5291f110e0Safresh1 5391f110e0Safresh1 if ($i >= @$fold_ref - 1) { # Final level: just return it 5491f110e0Safresh1 push @ret, "\"$new_string\""; 5591f110e0Safresh1 } 5691f110e0Safresh1 else { # Generate the combinations for the next level with this one's 5791f110e0Safresh1 push @ret, &gen_combinations($fold_ref, $new_string, $i + 1); 5891f110e0Safresh1 } 5991f110e0Safresh1 } 60*eac174f2Safresh1 } 6191f110e0Safresh1 6291f110e0Safresh1 return @ret; 6391f110e0Safresh1} 6491f110e0Safresh1 6556d68f1eSafresh1sub multi_char_folds ($$) { 6656d68f1eSafresh1 my $type = shift; # 'u' for UTF-8; 'l' for latin1 6756d68f1eSafresh1 my $range = shift; # 'a' for all; 'h' for starting 2 bytes; 'm' for ending 2 6856d68f1eSafresh1 die "[lu] only valid values for first parameter" if $type !~ /[lu]/; 6956d68f1eSafresh1 die "[aht3] only valid values for 2nd parameter" if $range !~ /[aht3]/; 7091f110e0Safresh1 71b8851fccSafresh1 return () if pack("C*", split /\./, Unicode::UCD::UnicodeVersion()) lt v3.0.1; 72b8851fccSafresh1 7391f110e0Safresh1 my ($cp_ref, $folds_ref, $format) = prop_invmap("Case_Folding"); 7491f110e0Safresh1 die "Could not find inversion map for Case_Folding" unless defined $format; 7591f110e0Safresh1 die "Incorrect format '$format' for Case_Folding inversion map" 7691f110e0Safresh1 unless $format eq 'al'; 7756d68f1eSafresh1 78*eac174f2Safresh1 my %inverse_latin1_folds; 79*eac174f2Safresh1 for my $i (0 .. @$cp_ref - 1) { 80*eac174f2Safresh1 next if ref $folds_ref->[$i]; # multi-char fold 81*eac174f2Safresh1 next if $folds_ref->[$i] == 0; # Not folded 82*eac174f2Safresh1 my $cp_base = $cp_ref->[$i]; 83*eac174f2Safresh1 84*eac174f2Safresh1 for my $j ($cp_base .. $cp_ref->[$i+1] - 1) { 85*eac174f2Safresh1 my $folded_base = $folds_ref->[$i]; 86*eac174f2Safresh1 next if $folded_base > 255; # only interested in Latin1 87*eac174f2Safresh1 push @{$inverse_latin1_folds{$folded_base + $j - $cp_base}}, $j; 88*eac174f2Safresh1 } 89*eac174f2Safresh1 } 90*eac174f2Safresh1 9191f110e0Safresh1 my @folds; 92*eac174f2Safresh1 my %output_folds; 9391f110e0Safresh1 9491f110e0Safresh1 for my $i (0 .. @$folds_ref - 1) { 9591f110e0Safresh1 next unless ref $folds_ref->[$i]; # Skip single-char folds 9691f110e0Safresh1 9791f110e0Safresh1 # The code in regcomp.c currently assumes that no multi-char fold 9891f110e0Safresh1 # folds to the upper Latin1 range. It's not a big deal to add; we 9991f110e0Safresh1 # just have to forbid such a fold in EXACTFL nodes, like we do already 10091f110e0Safresh1 # for ascii chars in EXACTFA (and EXACTFL) nodes. But I (khw) doubt 10191f110e0Safresh1 # that there will ever be such a fold created by Unicode, so the code 10291f110e0Safresh1 # isn't there to occupy space and time; instead there is this check. 1036fb12b70Safresh1 die sprintf("regcomp.c can't cope with a latin1 multi-char fold (found in the fold of 0x%X", $cp_ref->[$i]) if grep { $_ < 256 && chr($_) !~ /[[:ascii:]]/ } @{$folds_ref->[$i]}; 10491f110e0Safresh1 10556d68f1eSafresh1 @folds = @{$folds_ref->[$i]}; 10656d68f1eSafresh1 if ($range eq '3') { 10756d68f1eSafresh1 next if @folds < 3; 10856d68f1eSafresh1 } 10956d68f1eSafresh1 elsif ($range eq 'h') { 11056d68f1eSafresh1 pop @folds; 11156d68f1eSafresh1 } 11256d68f1eSafresh1 elsif ($range eq 't') { 11356d68f1eSafresh1 next if @folds < 3; 11456d68f1eSafresh1 shift @folds; 11556d68f1eSafresh1 } 11656d68f1eSafresh1 11791f110e0Safresh1 # Create a line that looks like "\x{foo}\x{bar}\x{baz}" of the code 11856d68f1eSafresh1 # points that make up the fold (use the actual character if 11956d68f1eSafresh1 # printable). 12056d68f1eSafresh1 my $fold = join "", map { chr $_ =~ /[[:print:]]/a 12156d68f1eSafresh1 ? chr $_ 12256d68f1eSafresh1 : sprintf "\\x{%X}", $_ 12356d68f1eSafresh1 } @folds; 12491f110e0Safresh1 $fold = "\"$fold\""; 12591f110e0Safresh1 12691f110e0Safresh1 # Skip if something else already has this fold 127*eac174f2Safresh1 next if grep { $_ eq $fold } keys %output_folds; 12891f110e0Safresh1 12956d68f1eSafresh1 my $this_fold_ref = \@folds; 13091f110e0Safresh1 for my $j (0 .. @$this_fold_ref - 1) { 13191f110e0Safresh1 my $this_ord = $this_fold_ref->[$j]; 13291f110e0Safresh1 undef $this_fold_ref->[$j]; 133*eac174f2Safresh1 134*eac174f2Safresh1 # If the fold is to a Latin1-range cased letter, replace the entry 135*eac174f2Safresh1 # with an array which also includes everything that folds to it. 136*eac174f2Safresh1 if (exists $inverse_latin1_folds{$this_ord}) { 137*eac174f2Safresh1 push @{$this_fold_ref->[$j]}, 138*eac174f2Safresh1 ( $this_ord, @{$inverse_latin1_folds{$this_ord}} ); 139*eac174f2Safresh1 } 140*eac174f2Safresh1 else { # Otherwise, just itself. (gen_combinations() needs a ref) 141*eac174f2Safresh1 @{$this_fold_ref->[$j]} = ( $this_ord ); 14291f110e0Safresh1 } 14391f110e0Safresh1 } 14491f110e0Safresh1 14591f110e0Safresh1 # Then generate all combinations of upper/lower case of the fold. 146*eac174f2Safresh1 $output_folds{$_} = $cp_ref->[$i] for gen_combinations($this_fold_ref); 14791f110e0Safresh1 } 14891f110e0Safresh1 1496fb12b70Safresh1 # \x17F is the small LONG S, which folds to 's'. Both Capital and small 1506fb12b70Safresh1 # LATIN SHARP S fold to 'ss'. Therefore, they should also match two 17F's 1516fb12b70Safresh1 # in a row under regex /i matching. But under /iaa regex matching, all 1526fb12b70Safresh1 # three folds to 's' are prohibited, but the sharp S's should still match 1536fb12b70Safresh1 # two 17F's. This prohibition causes our regular regex algorithm that 1546fb12b70Safresh1 # would ordinarily allow this match to fail. This is the only instance in 1556fb12b70Safresh1 # all Unicode of this kind of issue. By adding a special case here, we 1566fb12b70Safresh1 # can use the regular algorithm (with some other changes elsewhere as 1576fb12b70Safresh1 # well). 1586fb12b70Safresh1 # 1596fb12b70Safresh1 # It would be possible to re-write the above code to automatically detect 1606fb12b70Safresh1 # and handle this case, and any others that might eventually get added to 1616fb12b70Safresh1 # the Unicode standard, but I (khw) don't think it's worth it. I believe 1626fb12b70Safresh1 # that it's extremely unlikely that more folds to ASCII characters are 1636fb12b70Safresh1 # going to be added, and if I'm wrong, fold_grind.t has the intelligence 1646fb12b70Safresh1 # to detect them, and test that they work, at which point another special 1656fb12b70Safresh1 # case could be added here if necessary. 1666fb12b70Safresh1 # 1676fb12b70Safresh1 # No combinations of this with 's' need be added, as any of these 168b46d8ef2Safresh1 # containing 's' are prohibited under /iaa. 169*eac174f2Safresh1 $output_folds{"\"\x{17F}\x{17F}\""} = 0xDF if $type eq 'u' && $range eq 'a'; 1706fb12b70Safresh1 171*eac174f2Safresh1 return %output_folds; 17291f110e0Safresh1} 17391f110e0Safresh1 17491f110e0Safresh11 175