source: proto/charsetcompiler/UCD/UCD_properties.py @ 5159

Last change on this file since 5159 was 5159, checked in by cameron, 3 years ago

Support for enumeration basis sets

File size: 15.4 KB
Line 
1#
2# UCD_properties.py - parsing Unicode Character Database (UCD) files
3# and generating C headers for property data using a compact bitset
4# representation.
5#
6# Robert D. Cameron
7# January 2, 2015
8#
9# Licensed under Open Software License 3.0.
10#
11#
12import re, string, os.path, cformat
13from unicode_set import *
14from UCD_parser import *
15
16PropertyAliases_template = r"""
17namespace UCD {
18    enum property_t {
19        %s};
20    const static std::vector<std::string> property_enum_name = {
21        %s};
22    const static std::vector<std::string> property_full_name = {
23        %s};
24    static std::unordered_map<std::string, int> alias_map {{
25        %s}};
26}
27"""
28
29EnumeratedProperty_template = r"""
30    namespace %s_ns {
31        enum value_t {
32            %s};
33        const static std::vector<std::string> enum_names = {
34            %s};
35        const static std::vector<std::string> value_names = {
36            %s};
37        static std::unordered_map<std::string, int> aliases_only_map {{
38            %s}};
39    }
40"""
41
42CodepointProperties = ['scf', 'slc', 'suc', 'stc']
43
44class UCD_generator():
45    def __init__(self):
46        self.supported_props = []
47        self.property_data_headers = []
48        self.missing_specs = {}
49
50    def load_property_name_info(self):
51        (self.property_enum_name_list, self.full_name_map, self.property_lookup_map, self.property_kind_map) = parse_PropertyAlias_txt()
52
53    def generate_PropertyAliases_h(self):
54        f = cformat.open_header_file_for_write('PropertyAliases')
55        cformat.write_imports(f, ["<string>", "<unordered_map>", "<vector>"])
56        enum_text = cformat.multiline_fill(self.property_enum_name_list, ',', 8)
57        enum_text2 = cformat.multiline_fill(['"%s"' % e for e in self.property_enum_name_list], ',', 8)
58        full_name_text = cformat.multiline_fill(['"%s"' % self.full_name_map[e] for e in self.property_enum_name_list], ',', 8)
59        map_text = cformat.multiline_fill(['{"%s", %s}' % (k, self.property_lookup_map[k]) for k in sorted(self.property_lookup_map.keys())], ',', 8)
60        f.write(PropertyAliases_template % (enum_text, enum_text2, full_name_text, map_text))
61        cformat.close_header_file(f)
62
63    def load_property_value_info(self):
64        (self.property_value_list, self.property_value_enum_integer, self.property_value_full_name_map, self.property_value_lookup_map, self.missing_specs) = parse_PropertyValueAlias_txt(self.property_lookup_map)
65
66
67    def generate_PropertyValueAliases_h(self):
68        f = cformat.open_header_file_for_write('PropertyValueAliases')
69        cformat.write_imports(f, ['"PropertyAliases.h"', "<vector>", "<unordered_map>", "<string>"])
70        f.write("namespace UCD {\n")
71        #  Generate the aliases for all Binary properties.
72        enum_text = cformat.multiline_fill(['N', 'Y'], ',', 12)
73        enum_names = cformat.multiline_fill(['"N"', '"Y"'], ',', 12)
74        full_name_text = cformat.multiline_fill(['"No"', '"Yes"'], ',', 12)
75        binary_properties = ['{"n", N}', '{"y", Y}', '{"no", N}', '{"yes", Y}', '{"f", N}', '{"t", Y}', '{"false", N}', '{"true", Y}']
76        binary_map_text = cformat.multiline_fill(binary_properties, ',', 12)
77        f.write(EnumeratedProperty_template % ('Binary', enum_text, enum_names, full_name_text, binary_map_text))
78        #
79        for p in self.property_enum_name_list:
80           if self.property_value_list.has_key(p):
81              if not self.property_kind_map[p] == 'Binary':
82                  enum_text = cformat.multiline_fill(self.property_value_list[p], ',', 12)
83                  enum_names = cformat.multiline_fill(['"%s"' % s for s in self.property_value_list[p]], ',', 12)
84                  if p == 'ccc': # Special case: add numeric value information for ccc.
85                      enum_text += r"""
86        };
87        const uint16_t enum_val[] = {
88    """
89                      enum_text += "      " + cformat.multiline_fill(["%s" % (self.property_value_enum_integer[p][e]) for e in self.property_value_list['ccc']], ',', 12)
90                  full_names = [self.property_value_full_name_map[p][e] for e in self.property_value_list[p]]
91                  full_name_text = cformat.multiline_fill(['"%s"' % name for name in full_names], ',', 12)
92                  canon_full_names = [canonicalize(name) for name in full_names]
93                  canon_enums = [canonicalize(e) for e in self.property_value_list[p]]
94                  canon_keys = [canonicalize(k) for k in self.property_value_lookup_map[p].keys()]
95                  aliases_only = [k for k in canon_keys if not k in canon_enums + canon_full_names]
96                  map_text = cformat.multiline_fill(['{"%s", %s_ns::%s}' % (k, p.upper(), self.property_value_lookup_map[p][k]) for k in sorted(aliases_only)], ',', 12)
97                  f.write(EnumeratedProperty_template % (p.upper(), enum_text, enum_names, full_name_text, map_text))
98        f.write("}\n")
99        cformat.close_header_file(f)
100
101 
102    def generate_property_value_file(self, filename_root, property_code):
103        canon_map = self.property_value_lookup_map[property_code]
104        if self.missing_specs.has_key(property_code):
105            default_value = canon_map[canonicalize(self.missing_specs[property_code])]
106        else: default_value = None
107        vlist = self.property_value_list[property_code]
108        canon_map = self.property_value_lookup_map[property_code]
109        (prop_values, value_map) = parse_UCD_enumerated_property_map(property_code, vlist, canon_map, filename_root + '.txt', default_value)
110        independent_prop_values = len(prop_values)
111        for v in vlist:
112            if not v in prop_values:
113                #raise Exception("Property %s value %s missing" % (self.full_name_map[property_code], v))
114                print("Warning: property %s has no instance of value %s" % (property_code, v))
115                prop_values.append(v)
116        #
117        self.property_value_list[property_code] = prop_values
118        basename = os.path.basename(filename_root)
119        f = cformat.open_header_file_for_write(os.path.basename(filename_root))
120        cformat.write_imports(f, ['"PropertyObjects.h"', '"PropertyValueAliases.h"', '"unicode_set.h"'])
121        f.write("\nnamespace UCD {\n")
122        f.write("  namespace %s_ns {\n" % property_code.upper())
123        f.write("    const unsigned independent_prop_values = %s;\n" % independent_prop_values)
124        if property_code == 'gc':
125            # special logic for derived categories
126            value_map['LC'] = union_of_all([value_map[v] for v in ['Lu', 'Ll', 'Lt']])
127            value_map['L'] = union_of_all([value_map[v] for v in ['Lu', 'Ll', 'Lt', 'Lm', 'Lo']])
128            value_map['M'] = union_of_all([value_map[v] for v in ['Mn', 'Mc', 'Me']])
129            value_map['N'] = union_of_all([value_map[v] for v in ['Nd', 'Nl', 'No']])
130            value_map['P'] = union_of_all([value_map[v] for v in ['Pc', 'Pd', 'Ps', 'Pe', 'Pi', 'Pf', 'Po']])
131            value_map['S'] = union_of_all([value_map[v] for v in ['Sm', 'Sc', 'Sk', 'So']])
132            value_map['Z'] = union_of_all([value_map[v] for v in ['Zs', 'Zl', 'Zp']])
133            value_map['C'] = union_of_all([value_map[v] for v in ['Cc', 'Cf', 'Cs', 'Co', 'Cn']])
134        for v in prop_values:
135            f.write("    /** Code Point Ranges for %s\n    " % v)
136            f.write(cformat.multiline_fill(['[%s, %s]' % (lo, hi) for (lo, hi) in uset_to_range_list(value_map[v])], ',', 4))
137            f.write("**/\n")
138            f.write("    const UnicodeSet %s_Set \n" % v.lower())
139            f.write(value_map[v].showC(8) + ";\n")
140        print "%s: %s bytes" % (basename, sum([value_map[v].bytes() for v in value_map.keys()]))
141        set_list = ['&%s_Set' % v.lower() for v in prop_values]
142        f.write("    static EnumeratedPropertyObject property_object\n")
143        f.write("        {%s,\n" % property_code)
144        f.write("         %s_ns::independent_prop_values,\n" % property_code.upper())
145        f.write("         %s_ns::enum_names,\n" % property_code.upper())
146        f.write("         %s_ns::value_names,\n" % property_code.upper())
147        f.write("         %s_ns::aliases_only_map,\n" % property_code.upper())
148        f.write("         {")
149        f.write(cformat.multiline_fill(set_list, ',', 8))
150        f.write("\n         }};\n    }\n}\n")
151        cformat.close_header_file(f)
152        self.supported_props.append(property_code)
153        self.property_data_headers.append(basename)
154
155    def generate_ScriptExtensions_h(self):
156        filename_root = 'ScriptExtensions'
157        property_code = 'scx'
158        (prop_values, value_map) = parse_ScriptExtensions_txt(self.property_value_list['sc'], self.property_value_lookup_map['sc'])
159        basename = os.path.basename(filename_root)
160        f = cformat.open_header_file_for_write(basename)
161        cformat.write_imports(f, ['"PropertyObjects.h"', '"PropertyValueAliases.h"', '"unicode_set.h"'])
162        f.write("\nnamespace UCD {\n")
163        f.write("    namespace SCX_ns {\n")
164        for v in self.property_value_list['sc']:
165            f.write("        /** Code Point Ranges for %s\n        " % v)
166            f.write(cformat.multiline_fill(['[%s, %s]' % (lo, hi) for (lo, hi) in uset_to_range_list(value_map[v])], ',', 8))
167            f.write("**/\n")
168            f.write("        const UnicodeSet %s_Ext \n" % v.lower())
169            f.write(value_map[v].showC(12) + ";\n")
170        set_list = ['&%s_Ext' % v.lower() for v in self.property_value_list['sc']]
171        f.write("        static ExtensionPropertyObject property_object\n")
172        f.write("       {%s,\n" % property_code)
173        f.write("        UCD::sc,\n")
174        f.write("       {")
175        f.write(cformat.multiline_fill(set_list, ',', 8))
176        f.write("\n        }};\n    }\n}\n")
177        cformat.close_header_file(f)
178        print "%s: %s bytes" % (basename, sum([value_map[v].bytes() for v in value_map.keys()]))
179        self.supported_props.append(property_code)
180        self.property_data_headers.append(basename)
181
182    def generate_binary_properties_file(self, filename_root):
183        (props, prop_map) = parse_UCD_codepoint_name_map(filename_root + '.txt', self.property_lookup_map)
184        basename = os.path.basename(filename_root)
185        f = cformat.open_header_file_for_write(basename)
186        cformat.write_imports(f, ['"PropertyAliases.h"', '"unicode_set.h"', "<vector>"])
187        f.write("\nnamespace UCD {\n")
188        for p in sorted(props):
189            # f.write("  namespace %s_ns {\n    const UnicodeSet codepoint_set \n" % p.upper())
190            # f.write(prop_map[p].showC(12) + ";\n")
191            # f.write("    static BinaryPropertyObject property_object{%s, codepoint_set};\n  }\n" % p)
192            f.write("    namespace %s_ns {\n" % p.upper())
193            f.write("        /** Code Point Ranges for %s\n        " % p)
194            f.write(cformat.multiline_fill(['[%s, %s]' % (lo, hi) for (lo, hi) in uset_to_range_list(prop_map[p])], ',', 8))
195            f.write("**/\n")
196            f.write("        const UnicodeSet codepoint_set \n")
197            f.write(prop_map[p].showC(12) + ";\n")
198            f.write("        static BinaryPropertyObject property_object{%s, codepoint_set};\n    }\n" % p)
199        f.write("}\n\n")
200        cformat.close_header_file(f)
201        print "%s: %s bytes" % (basename, sum([prop_map[p].bytes() for p in prop_map.keys()]))
202        self.supported_props += props
203        self.property_data_headers.append(basename)
204
205    def generate_PropertyObjectTable_h(self):
206        f = cformat.open_header_file_for_write('PropertyObjectTable')
207        cformat.write_imports(f, ['"PropertyObjects.h"', '"PropertyAliases.h"', '<array>'])
208        cformat.write_imports(f, ['"%s.h"' % fname for fname in self.property_data_headers])
209        f.write("\nnamespace UCD {\n")
210        objlist = []
211        for p in self.property_enum_name_list:
212            k = self.property_kind_map[p]
213            if p in self.supported_props:
214                objlist.append("&%s_ns::property_object" % p.upper())
215            elif k == 'String':
216                if p in CodepointProperties:
217                    objlist.append("new UnsupportedPropertyObject(%s, PropertyObject::ClassTypeId::CodepointProperty)" % p)
218                else:
219                    objlist.append("new UnsupportedPropertyObject(%s, PropertyObject::ClassTypeId::StringProperty)" % p)
220            else:
221                objlist.append("new UnsupportedPropertyObject(%s, PropertyObject::ClassTypeId::%sProperty)" % (p, k))
222        f.write("\n  const std::array<PropertyObject *, %i> property_object_table = {{\n    " % len(objlist))
223        f.write(",\n    ".join(objlist) + '  }};\n}\n')
224        cformat.close_header_file(f)
225
226
227
228def UCD_main():
229    ucd = UCD_generator()
230
231    # First parse all property names and their aliases
232    ucd.load_property_name_info()
233    #
234    # Generate the PropertyAliases.h file to define all the Unicode property_t enum
235    # and the basic property information.
236    ucd.generate_PropertyAliases_h()
237    #
238    # Next parse all property value names and their aliases.  Generate the data.
239    ucd.load_property_value_info()
240    #
241    # The Age property
242    ucd.generate_property_value_file('DerivedAge', 'age')
243    #
244    # The Block property
245    ucd.generate_property_value_file('Blocks', 'blk')
246    #
247    # Scripts
248    ucd.generate_property_value_file('Scripts', 'sc')
249    #
250    # Script Extensions
251    ucd.generate_ScriptExtensions_h()
252    #
253    # General Category
254    ucd.generate_property_value_file('extracted/DerivedGeneralCategory', 'gc')
255    #
256    # Binary properties from PropList.txt
257    ucd.generate_binary_properties_file('PropList')
258    #
259    # Binary properties from DerivedCoreProperties.txt
260    ucd.generate_binary_properties_file('DerivedCoreProperties')
261    #
262    #
263    # LineBreak types
264    #ucd.generate_property_value_file('extracted/DerivedLineBreak', 'lb')
265    ucd.generate_property_value_file('LineBreak', 'lb')
266    #
267    # Grapheme Cluster Break property
268    ucd.generate_property_value_file('auxiliary/GraphemeBreakProperty', 'GCB')
269    #
270    # Sentence Break property
271    ucd.generate_property_value_file('auxiliary/SentenceBreakProperty', 'SB')
272    #
273    # Word Break property
274    ucd.generate_property_value_file('auxiliary/WordBreakProperty', 'WB')
275    #
276    # East Asian Width
277    ucd.generate_property_value_file('EastAsianWidth', 'ea')
278    #ucd.generate_property_value_file('extracted/DerivedEastAsianWidth', 'ea')
279    #
280    # Hangul Syllable Type
281    ucd.generate_property_value_file('HangulSyllableType', 'hst')
282    #
283    # Bidi_Class
284    ucd.generate_property_value_file('extracted/DerivedBidiClass', 'bc')
285    #
286    # Bidi Mirroroing from DerivedCoreProperties.txt
287    ucd.generate_binary_properties_file('extracted/DerivedBinaryProperties')
288    #
289    # Canonical_Combining_Class
290    ucd.generate_property_value_file('extracted/DerivedCombiningClass', 'ccc')
291    #
292    # Decomposition Type
293    ucd.generate_property_value_file('extracted/DerivedDecompositionType', 'dt')
294    #
295    # Joining Group and Type
296    ucd.generate_property_value_file('extracted/DerivedJoiningGroup', 'jg')
297    ucd.generate_property_value_file('extracted/DerivedJoiningType', 'jt')
298    #
299    # Numeric Type and Value
300    ucd.generate_property_value_file('extracted/DerivedNumericType', 'nt')
301    #ucd.generate_property_value_file('extracted/DerivedNumericValue', 'nv')
302    #
303    # Binary normalization properties.
304    ucd.generate_binary_properties_file('DerivedNormalizationProps')
305
306    #
307    # Jamo Short Name - AAARGH - property value for 110B is an empty string!!!!!  - Not in PropertyValueAliases.txt
308    # ucd.generate_property_value_file('Jamo', 'jsn')
309    #
310    #
311    #
312    ucd.generate_PropertyValueAliases_h()
313
314    ucd.generate_PropertyObjectTable_h()
315
316if __name__ == "__main__":
317  UCD_main()
Note: See TracBrowser for help on using the repository browser.