browser: replace smartmatch by equivalent syntax
[sheet.git] / tools / mkcharinfo
1 #!/usr/bin/env perl
2 use 5.014;
3 use warnings;
4 use utf8;
5 use lib '.';
6
7 use open OUT => ':encoding(utf-8)', ':std';
8 use Data::Dump 'pp';
9
10 our $VERSION = '1.03';
11
12 my %info = (
13         # prepare presentational string for some control(lish) entries
14         "\xAD"     => {string => '-'},
15         "\x{200E}" => {string => '→'},
16         "\x{200F}" => {string => '←'},
17         "\x{200B}" => {string => '␣'}, # nbsp: ~ in TeX
18         "\x{200C}" => {string => '|'}, # ISO-9995-7-081 lookalike (alt: ∣ ⊺ ⟙)
19         "\x{200D}" => {string => '⁀'}, # join (alt: ∤ |ͯ ⨝)
20         (map {( $_ => {string => chr(9676).$_.chr(9676)} )} map {chr} # combining double
21                 0x35C .. 0x362, 0x1DCD, 0x1DFC,
22         ),
23 );
24 $info{chr $_} //= {} for 32 .. 126;
25
26 eval {
27         my $tables = do './unicode-table.inc.pl' or die $@ || $!;
28         for (values %$tables) {
29                 for (values %$_) {
30                         for (@$_) {
31                                 length $_ == 1 or next;  # ignore meta values
32                                 s/\\//;  # unescape
33                                 $info{$_} //= {};
34                         }
35                 }
36         }
37         1;
38 } or warn "Failed reading unicode tables: $@";
39
40 for my $layout ('macos-abc', 'windows') {
41         eval {
42                 my $kbd = do "./keyboard/altgr/$layout.eng.inc.pl" or die $@ || $!;
43                 $info{$_} //= {} for map {s/◌//g; m/\A./g} values %{ $kbd->{key} };
44                 1;
45         } or warn "Failed reading additional keyboard map $layout: $@";
46 }
47
48 eval {
49         require HTML::Entities;
50         our %char2entity;
51         HTML::Entities->import('%char2entity');
52         while (my ($char, $entity) = each %char2entity) {
53                 $entity =~ /[a-zA-Z]/ or next;  # only actual aliases
54                 $info{$char}->{html} = substr($entity, 1, -1);
55         }
56         1;
57 } or warn "Failed importing html entities: $@";
58
59 my %diinc = (
60         './data/digraphs-rfc.inc.pl' => 'u-di',
61         './data/digraphs-shiar.inc.pl' => 'u-prop',
62         './data/digraphs-vim.inc.pl' => 'u-vim',
63 );
64 for (sort keys %diinc) {
65         -e $_ or next;
66         my $di = do $_ or die "Error reading digraphs file $_: ", $@ || $!;
67         for my $mnem (sort keys %{$di}) {
68                 my $cp = $di->{$mnem};
69                 length $mnem == 2 or next;  # limit to digraphs
70                 my $class = $diinc{$_};
71                 $info{$cp}->{di} //= $mnem;
72                 $info{$cp}->{class}->{$class}++;
73         }
74 }
75
76 eval {
77         # read introducing unicode versions for known characters
78         my $agemap = do './data/unicode-age.inc.pl' or die $@ || $!;
79         for my $chr (keys %info) {
80                 my $version = $agemap->{ord $chr} or next;
81                 $info{$chr}->{class}->{'u-v'.$version}++
82         }
83         1;
84 } or warn "Failed including unicode version data: $@";
85
86 for my $chr (keys %info) {
87         my $cp = ord $chr;
88         #my $info = glyph_mkinfo($cp) or next;
89         # attempt to get unicode character information
90         my $info = eval {
91                 require Unicode::UCD;
92                 Unicode::UCD::charinfo($cp)
93                         || { block => '?', category => 'Xn', name => '', script => '' }
94         } or next;
95
96         $info->{$_} = $info{$chr}->{$_} for keys %{ $info{$chr} };
97
98         # ignore vim flag in addition to rfc support, replace otherwise
99         $info->{class}->{'u-di'} or $info->{class}->{'u-prop'}++
100                 if delete $info->{class}->{'u-vim'};
101
102         # categorise by unicode types and writing script
103         $info->{class}->{$_}++ for $info->{category};
104         $info->{class}->{$_}++ for $info->{script} || ();
105
106         # add custom categories for certain blocks
107         $info->{class}->{Xa}++ if $info->{block} eq 'Basic Latin';
108         $info->{class}->{Xl}++ if $info->{block} eq 'Latin-1 Supplement';
109
110         {
111                 if ($info->{string}) {
112                         # keep predefined presentational string
113                 }
114                 elsif ($info->{combining}) {
115                         # overlay combining accents
116                         $info->{string} = chr(9676) . $chr;
117                 }
118                 elsif (($cp & ~0b1001_1111) == 0 or $cp == 127) {
119                         # control characters (first 32 chars from 0 and 128)
120                         # rename to something more descriptive
121                         $info->{name} = $info->{unicode10}
122                                 ? '<'.$info->{unicode10}.'>'  # the old name was much more useful
123                                 : sprintf('<control U+%04X>', $cp);  # at least identify by value
124                         # show descriptive symbols instead of control chars themselves
125                         $info->{string} = $cp < 32   ? chr($cp + 0x2400) :
126                                           $cp == 127 ? chr(0x2421) :
127                                                        chr(0xFFFD);
128                 }
129         }
130
131         $info{$chr} = $info;
132 }
133
134 # output perl code of hash
135 say "# automatically generated by $0";
136 say 'use utf8;';
137 say '+{';
138 for my $cp (sort keys %info) {
139         $info{$cp}->{classstr} = join(' ', sort keys %{ $info{$cp}->{class} });
140         # convert info hashes into arrays of strings to output in display order
141         my $row = [ map { $info{$cp}->{$_} } qw/classstr name di html string/ ];
142         # strip off trailing missing values (especially string may be unknown)
143         defined $row->[-1] ? last : pop @$row for 1 .. @$row;
144         # final line (assume safe within single quotes)
145         say sprintf '"\x{%X}" => [%s],',
146                 ord $cp, join(',', map { escapeq($_) } @$row);
147 }
148 say '}';
149
150 sub escapeq {
151         local $_ = shift;
152         return 'undef' if not defined;
153         s/(['\\])/\\$1/g;
154         return "'$_'";
155 }
156
157 __END__
158
159 =head1 NAME
160
161 mkcharinfo - Gather Unicode character details in Perl array
162
163 =head1 SYNOPSIS
164
165     mkcharinfo > unicode-char.inc.pl
166
167 Test by printing the description of U+0041 (latin A):
168
169     perl -e'$u = do "unicode-char.inc.pl"; print $u->{A}->[1]'
170
171 =head1 AUTHOR
172
173 Mischa POSLAWSKY <perl@shiar.org>
174
175 =head1 LICENSE
176
177 Licensed under the GNU Affero General Public License version 3.
178