tools/mkcharinfo: silence warnings in perl 5.18
[sheet.git] / tools / mkcharinfo
1 #!/usr/bin/env perl
2 use 5.010;
3 use strict;
4 use warnings;
5 use utf8;
6 no if $] >= 5.018, warnings => 'experimental::smartmatch';
7
8 use open OUT => ':utf8', ':std';
9 use Data::Dump 'pp';
10
11 our $VERSION = '1.00';
12
13 my %info = (
14         "\xAD"     => {string => '-'},
15         "\x{200E}" => {string => '→'},
16         "\x{200F}" => {string => '←'},
17 );
18 $info{chr $_} //= {} for 32 .. 126;
19
20 eval {
21         my $tables = do 'unicode-table.inc.pl' or die $@ || $!;
22         for (values %$tables) {
23                 for (values %$_) {
24                         for (@$_) {
25                                 length $_ == 1 or next;  # ignore meta values
26                                 s/\\//;  # unescape
27                                 $info{$_} //= {};
28                         }
29                 }
30         }
31         1;
32 } or warn "Failed reading unicode tables: $@";
33
34 eval {
35         require HTML::Entities;
36         our %char2entity;
37         HTML::Entities->import('%char2entity');
38         while (my ($char, $entity) = each %char2entity) {
39                 $entity =~ /[a-zA-Z]/ or next;  # only actual aliases
40                 $info{$char}->{html} = substr($entity, 1, -1);
41         }
42         1;
43 } or warn "Failed importing html entities: $@";
44
45 my %diinc = (
46         'digraphs.inc.pl' => 'u-di',
47 );
48 for (keys %diinc) {
49         -e $_ or next;
50         my $di = do $_ or die "Error reading digraphs file $_: ", $@ || $!;
51         while (my ($mnem, $cp) = each %$di) {
52                 length $mnem == 2 or next;  # limit to digraphs
53                 my $class = $diinc{$_};
54                 if (ref $cp) {
55                         # old style array
56                         $class = 'u-prop' if $cp->[2] and $cp->[2] =~ m/\bXz\b/;
57                         $cp = chr $cp->[0];
58                 }
59                 $info{$cp}->{di} //= $mnem;
60                 $info{$cp}->{class}->{$class}++;
61         }
62 }
63
64 for my $chr (keys %info) {
65         my $cp = ord $chr;
66         # attempt to get unicode character information
67         my $info = eval {
68                 require Unicode::UCD;
69                 Unicode::UCD::charinfo($cp)
70                         || { block => '?', category => 'Xn', name => '', script => '' }
71         } or next;
72
73         $info->{$_} = $info{$chr}->{$_} for qw(di html class string);
74
75         # categorise by unicode types and writing script
76         $info->{class}->{$_}++ for $info->{category};
77         $info->{class}->{$_}++ for $info->{script} || ();
78
79         # add custom categories for certain blocks
80         $info->{class}->{Xa}++ if $info->{block} eq 'Basic Latin';
81         $info->{class}->{Xl}++ if $info->{block} eq 'Latin-1 Supplement';
82
83         given ($cp) {
84                 when ($info->{string}) {
85                         # keep predefined presentational string
86                 }
87                 when ($info->{combining}) {
88                         # overlay combining accents
89                         $info->{string} = chr(9676) . $chr;
90                 }
91                 when (($cp & ~0b1001_1111) == 0 or $cp == 127) {
92                         # control characters (first 32 chars from 0 and 128)
93                         # rename to something more descriptive
94                         $info->{name} = $info->{unicode10}
95                                 ? '<'.$info->{unicode10}.'>'  # the old name was much more useful
96                                 : sprintf('<control U+%04X>', $cp);  # at least identify by value
97                         # show descriptive symbols instead of control chars themselves
98                         $info->{string} = $cp < 32 ? chr($cp + 0x2400) : chr(0xFFFD);
99                 }
100         }
101
102         $info{$chr} = $info;
103 }
104
105 # output perl code of hash
106 say 'use utf8;';
107 say '+{';
108 for my $cp (sort keys %info) {
109         $info{$cp}->{classstr} = join(' ', sort keys %{ $info{$cp}->{class} });
110         # convert info hashes into arrays of strings to output in display order
111         my $row = [ map { $info{$cp}->{$_} } qw/classstr name di html string/ ];
112         # strip off trailing missing values (especially string may be unknown)
113         defined $row->[-1] ? last : pop @$row for 1 .. @$row;
114         # final line (assume safe within single quotes)
115         say sprintf '"\x{%X}" => [%s],',
116                 ord $cp, join(',', map { escapeq($_) } @$row);
117 }
118 say '}';
119
120 sub escapeq {
121         local $_ = shift;
122         return 'undef' if not defined;
123         s/(['\\])/\\$1/g;
124         return "'$_'";
125 }
126
127 __END__
128
129 =head1 NAME
130
131 mkcharinfo - Gather Unicode character details in Perl array
132
133 =head1 SYNOPSIS
134
135     mkcharinfo > unicode-char.inc.pl
136
137 Test by printing the description of U+0041 (latin A):
138
139     perl -e'$u = do "unicode-char.inc.pl"; print $u->{A}->[1]'
140
141 =head1 AUTHOR
142
143 Mischa POSLAWSKY <perl@shiar.org>
144
145 =head1 LICENSE
146
147 Licensed under the GNU Affero General Public License version 3.
148