This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
utf8.h: Add some casts in macros, for safety
[perl5.git] / regen / unicode_constants.pl
1 use v5.16.0;
2 use strict;
3 use warnings;
4 require 'regen/regen_lib.pl';
5 require 'regen/charset_translations.pl';
6 use Unicode::UCD;
7 use charnames qw(:loose);
8
9 my $out_fh = open_new('unicode_constants.h', '>',
10         {style => '*', by => $0,
11                       from => "Unicode data"});
12
13 print $out_fh <<END;
14
15 #ifndef H_UNICODE_CONSTANTS   /* Guard against nested #includes */
16 #define H_UNICODE_CONSTANTS   1
17
18 /* This file contains #defines for the version of Unicode being used and
19  * various Unicode code points.  The values the code point macros expand to
20  * are the native Unicode code point, or all or portions of the UTF-8 encoding
21  * for the code point.  In the former case, the macro name has the suffix
22  * "_NATIVE"; otherwise, the suffix "_UTF8".
23  *
24  * The macros that have the suffix "_UTF8" may have further suffixes, as
25  * follows:
26  *  "_FIRST_BYTE" if the value is just the first byte of the UTF-8
27  *                representation; the value will be a numeric constant.
28  *  "_TAIL"       if instead it represents all but the first byte.  This, and
29  *                with no additional suffix are both string constants */
30
31 END
32
33 my $version = Unicode::UCD::UnicodeVersion();
34 my ($major, $dot, $dotdot) = $version =~ / (.*?) \. (.*?) (?: \. (.*) )? $ /x;
35 $dotdot = 0 unless defined $dotdot;
36
37 print $out_fh <<END;
38 #define UNICODE_MAJOR_VERSION   $major
39 #define UNICODE_DOT_VERSION     $dot
40 #define UNICODE_DOT_DOT_VERSION $dotdot
41
42 END
43
44 # The data are at __DATA__  in this file.
45
46 my @data = <DATA>;
47
48 foreach my $charset (get_supported_code_pages()) {
49     print $out_fh "\n" . get_conditional_compile_line_start($charset);
50
51     my @a2n = @{get_a2n($charset)};
52
53     for ( @data ) {
54         chomp;
55
56         # Convert any '#' comments to /* ... */; empty lines and comments are
57         # output as blank lines
58         if ($_ =~ m/ ^ \s* (?: \# ( .* ) )? $ /x) {
59             my $comment_body = $1 // "";
60             if ($comment_body ne "") {
61                 print $out_fh "/* $comment_body */\n";
62             }
63             else {
64                 print $out_fh "\n";
65             }
66             next;
67         }
68
69         unless ($_ =~ m/ ^ ( [^\ ]* )           # Name or code point token
70                         (?: [\ ]+ ( [^ ]* ) )?  # optional flag
71                         (?: [\ ]+ ( .* ) )?  # name if unnamed; flag is required
72                     /x)
73         {
74             die "Unexpected syntax at line $.: $_\n";
75         }
76
77         my $name_or_cp = $1;
78         my $flag = $2;
79         my $desired_name = $3;
80
81         my $name;
82         my $cp;
83         my $U_cp;   # code point in Unicode (not-native) terms
84
85         if ($name_or_cp =~ /^U\+(.*)/) {
86             $U_cp = hex $1;
87             $name = charnames::viacode($name_or_cp);
88             if (! defined $name) {
89                 next if $flag =~ /skip_if_undef/;
90                 die "Unknown code point '$name_or_cp' at line $.: $_\n" unless $desired_name;
91                 $name = "";
92             }
93         }
94         else {
95             $name = $name_or_cp;
96             die "Unknown name '$name' at line $.: $_\n" unless defined $name;
97             $U_cp = charnames::vianame($name =~ s/_/ /gr);
98         }
99
100         $cp = ($U_cp < 256)
101             ? $a2n[$U_cp]
102             : $U_cp;
103
104         $name = $desired_name if $name eq "" && $desired_name;
105         $name =~ s/[- ]/_/g;   # The macro name can have no blanks nor dashes
106
107         my $str;
108         my $suffix;
109         if (defined $flag && $flag eq 'native') {
110             die "Are you sure you want to run this on an above-Latin1 code point?" if $cp > 0xff;
111             $suffix = '_NATIVE';
112             $str = sprintf "0x%02X", $cp;        # Is a numeric constant
113         }
114         else {
115             $str = join "", map { sprintf "\\x%02X", ord $_ } split //, cp_2_utfbytes($U_cp, $charset);
116
117             $suffix = '_UTF8';
118             if (! defined $flag || $flag =~ /^ string (_skip_if_undef)? $/x) {
119                 $str = "\"$str\"";  # Will be a string constant
120             } elsif ($flag eq 'tail') {
121                     $str =~ s/\\x..//;  # Remove the first byte
122                     $suffix .= '_TAIL';
123                     $str = "\"$str\"";  # Will be a string constant
124             }
125             elsif ($flag eq 'first') {
126                 $str =~ s/ \\x ( .. ) .* /$1/x; # Get the two nibbles of the 1st byte
127                 $suffix .= '_FIRST_BYTE';
128                 $str = "0x$str";        # Is a numeric constant
129             }
130             else {
131                 die "Unknown flag at line $.: $_\n";
132             }
133         }
134         printf $out_fh "#   define %s%s  %s    /* U+%04X */\n", $name, $suffix, $str, $U_cp;
135     }
136
137     my $max_PRINT_A = 0;
138     for my $i (0x20 .. 0x7E) {
139         $max_PRINT_A = $a2n[$i] if $a2n[$i] > $max_PRINT_A;
140     }
141     printf $out_fh "#   define MAX_PRINT_A_FOR_USE_ONLY_BY_REGCOMP_DOT_C   0x%02X   /* The max code point that isPRINT_A */\n", $max_PRINT_A;
142
143     print $out_fh "\n" . get_conditional_compile_line_end();
144
145 }
146
147 use Unicode::UCD 'prop_invlist';
148
149 my $count = 0;
150 my @other_invlist = prop_invlist("Other");
151 for (my $i = 0; $i < @other_invlist; $i += 2) {
152     $count += ((defined $other_invlist[$i+1])
153               ? $other_invlist[$i+1]
154               : 0x110000)
155               - $other_invlist[$i];
156 }
157 printf $out_fh "\n/* The number of code points not matching \\pC */\n"
158              . "#define NON_OTHER_COUNT_FOR_USE_ONLY_BY_REGCOMP_DOT_C  %d\n",
159             0x110000 - $count;
160
161 print $out_fh "\n#endif /* H_UNICODE_CONSTANTS */\n";
162
163 read_only_bottom_close_and_rename($out_fh);
164
165 # DATA FORMAT
166 #
167 # A blank line is output as-is.
168 # Comments (lines whose first non-blank is a '#') are converted to C-style,
169 # though empty comments are converted to blank lines.  Otherwise, each line
170 # represents one #define, and begins with either a Unicode character name with
171 # the blanks and dashes in it squeezed out or replaced by underscores; or it
172 # may be a hexadecimal Unicode code point of the form U+xxxx.  In the latter
173 # case, the name will be looked-up to use as the name of the macro.  In either
174 # case, the macro name will have suffixes as listed above, and all blanks and
175 # dashes will be replaced by underscores.
176 #
177 # Each line may optionally have one of the following flags on it, separated by
178 # white space from the initial token.
179 #   string  indicates that the output is to be of the string form
180 #           described in the comments above that are placed in the file.
181 #   string_skip_ifundef  is the same as 'string', but instead of dying if the
182 #           code point doesn't exist, the line is just skipped: no output is
183 #           generated for it
184 #   first   indicates that the output is to be of the FIRST_BYTE form.
185 #   tail    indicates that the output is of the _TAIL form.
186 #   native  indicates that the output is the code point, converted to the
187 #           platform's native character set if applicable
188 #
189 # If the code point has no official name, the desired name may be appended
190 # after the flag, which will be ignored if there is an official name.
191 #
192 # This program is used to make it convenient to create compile time constants
193 # of UTF-8, and to generate proper EBCDIC as well as ASCII without manually
194 # having to figure things out.
195
196 __DATA__
197 U+017F string
198
199 U+0300 string
200
201 U+0399 string
202 U+03BC string
203
204 U+1E9E string_skip_if_undef
205
206 U+FB05 string
207 U+FB06 string
208 U+0130 string
209 U+0131 string
210
211 U+2010 string
212 BOM first
213 BOM tail
214
215 NBSP native
216 NBSP string
217
218 DEL native
219 CR  native
220 LF  native
221 VT  native
222 ESC native
223 U+00DF native
224 U+00E5 native
225 U+00C5 native
226 U+00FF native
227 U+00B5 native