1 package regcharclass_multi_char_folds;
5 use Unicode::UCD "prop_invmap";
7 # This returns an array of strings of the form
8 # "\x{foo}\x{bar}\x{baz}"
9 # of the sequences of code points that are multi-character folds in the
10 # current Unicode version. If the parameter is 1, all such folds are
11 # returned. If the parameters is 0, only the ones containing exclusively
12 # Latin1 characters are returned. In the latter case all combinations of
13 # Latin1 characters that can fold to the base one are returned. Thus for
14 # 'ss', it would return in addition, 'Ss', 'sS', and 'SS'. This is because
15 # this code is designed to help regcomp.c, and EXACTFish regnodes. For
16 # non-UTF-8 patterns, the strings are not necessarily folded, so we need to
17 # check for the upper and lower case versions. For UTF-8 patterns, the
18 # strings are folded, except in EXACTFL nodes) so we only need to worry about
19 # the fold version. All folded-to characters in non-UTF-8 (Latin1) are
20 # members of fold-pairs, at least within Latin1, 'k', and 'K', for example.
21 # So there aren't complications with dealing with unfolded input. That's not
22 # true of UTF-8 patterns, where things can get tricky. Thus for EXACTFL nodes
23 # where things aren't all folded, code has to be written specially to handle
24 # this, instead of the macros here being extended to try to handle it.
26 # There are no non-ASCII Latin1 multi-char folds currently, and none likely to
27 # be ever added. Thus the output is the same as if it were just asking for
28 # ASCII characters, not full Latin1. Hence, it is suitable for generating
29 # things that match EXACTFAA. It does check for and croak if there ever were
30 # to be an upper Latin1 range multi-character fold.
32 # This is designed for input to regen/regcharlass.pl.
34 sub gen_combinations ($;) {
35 # Generate all combinations for the first parameter which is an array of
38 my ($fold_ref, $string, $i) = @_;
39 $string = "" unless $string;
44 # Look at each element in this level's array.
45 foreach my $j (0 .. @{$fold_ref->[$i]} - 1) {
47 # Append its representation to what we have currently
48 my $new_string = $fold_ref->[$i][$j] =~ /[[:print:]]/
49 ? ($string . chr $fold_ref->[$i][$j])
50 : sprintf "$string\\x{%X}", $fold_ref->[$i][$j];
52 if ($i >= @$fold_ref - 1) { # Final level: just return it
53 push @ret, "\"$new_string\"";
55 else { # Generate the combinations for the next level with this one's
56 push @ret, &gen_combinations($fold_ref, $new_string, $i + 1);
63 sub multi_char_folds ($) {
64 my $all_folds = shift; # The single parameter is true if wants all
65 # multi-char folds; false if just the ones that
68 return () if pack("C*", split /\./, Unicode::UCD::UnicodeVersion()) lt v3.0.1;
70 my ($cp_ref, $folds_ref, $format) = prop_invmap("Case_Folding");
71 die "Could not find inversion map for Case_Folding" unless defined $format;
72 die "Incorrect format '$format' for Case_Folding inversion map"
73 unless $format eq 'al';
76 for my $i (0 .. @$folds_ref - 1) {
77 next unless ref $folds_ref->[$i]; # Skip single-char folds
79 # The code in regcomp.c currently assumes that no multi-char fold
80 # folds to the upper Latin1 range. It's not a big deal to add; we
81 # just have to forbid such a fold in EXACTFL nodes, like we do already
82 # for ascii chars in EXACTFA (and EXACTFL) nodes. But I (khw) doubt
83 # that there will ever be such a fold created by Unicode, so the code
84 # isn't there to occupy space and time; instead there is this check.
85 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]};
87 # Create a line that looks like "\x{foo}\x{bar}\x{baz}" of the code
88 # points that make up the fold (use the actual character if
90 my $fold = join "", map { chr $_ =~ /[[:print:]]/a
92 : sprintf "\\x{%X}", $_
93 } @{$folds_ref->[$i]};
96 # Skip if something else already has this fold
97 next if grep { $_ eq $fold } @folds;
101 } # Skip if wants only all-ascii folds, and there is a non-ascii
102 elsif (! grep { chr($_) =~ /[^[:ascii:]]/ } @{$folds_ref->[$i]}) {
104 # If the fold is to a cased letter, replace the entry with an
105 # array which also includes its upper case.
106 my $this_fold_ref = $folds_ref->[$i];
107 for my $j (0 .. @$this_fold_ref - 1) {
108 my $this_ord = $this_fold_ref->[$j];
109 if (chr($this_ord) =~ /\p{Cased}/) {
110 my $uc = ord(uc(chr($this_ord)));
111 undef $this_fold_ref->[$j];
112 @{$this_fold_ref->[$j]} = ( $this_ord, $uc);
116 # Then generate all combinations of upper/lower case of the fold.
117 push @folds, gen_combinations($this_fold_ref);
122 # \x17F is the small LONG S, which folds to 's'. Both Capital and small
123 # LATIN SHARP S fold to 'ss'. Therefore, they should also match two 17F's
124 # in a row under regex /i matching. But under /iaa regex matching, all
125 # three folds to 's' are prohibited, but the sharp S's should still match
126 # two 17F's. This prohibition causes our regular regex algorithm that
127 # would ordinarily allow this match to fail. This is the only instance in
128 # all Unicode of this kind of issue. By adding a special case here, we
129 # can use the regular algorithm (with some other changes elsewhere as
132 # It would be possible to re-write the above code to automatically detect
133 # and handle this case, and any others that might eventually get added to
134 # the Unicode standard, but I (khw) don't think it's worth it. I believe
135 # that it's extremely unlikely that more folds to ASCII characters are
136 # going to be added, and if I'm wrong, fold_grind.t has the intelligence
137 # to detect them, and test that they work, at which point another special
138 # case could be added here if necessary.
140 # No combinations of this with 's' need be added, as any of these
141 # containing 's' are prohibited under /iaa.
142 push @folds, '"\x{17F}\x{17F}"' if $all_folds;