source: icGREP/icgrep-devel/UCD-scripts/UCD_properties.py @ 5671

Last change on this file since 5671 was 5671, checked in by cameron, 22 months ago

Name_Alias property

File size: 22.1 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, UCD_config
13from unicode_set import *
14from UCD_parser import *
15from UCD_property_objects import *
16
17PropertyAliases_template = r"""
18namespace UCD {
19    enum property_t {
20        %s};
21    const static std::vector<std::string> property_enum_name = {
22        %s};
23    const static std::vector<std::string> property_full_name = {
24        %s};
25    static std::unordered_map<std::string, int> alias_map {{
26        %s}};
27}
28"""
29
30EnumeratedProperty_template = r"""
31    namespace %s_ns {
32        enum value_t {
33            %s};
34        const static std::vector<std::string> enum_names = {
35            %s};
36        const static std::vector<std::string> value_names = {
37            %s};
38        static std::unordered_map<std::string, int> aliases_only_map {{
39            %s}};
40    }
41"""
42
43def emit_string_property(f, property_code, null_set, reflexive_set, cp_value_map):
44    s = string.Template(r"""    namespace ${prop_enum_up}_ns {
45        /** Code Point Ranges for ${prop_enum} mapping to <none>
46        ${null_set_ranges}**/
47
48        const UnicodeSet null_codepoint_set
49        ${null_set_value};
50
51        /** Code Point Ranges for ${prop_enum} mapping to <codepoint>
52        ${reflexive_set_ranges}**/
53        const UnicodeSet reflexive_set
54        ${reflexive_set_value};
55
56        const unsigned buffer_length = ${buffer_length};
57        const static char __attribute__ ((aligned (32))) string_buffer[${allocation_length}] = u8R"__(${string_buffer})__";
58
59        const static std::vector<codepoint_t> defined_cps = {
60        ${explicitly_defined_cps}};
61        static StringPropertyObject property_object(${prop_enum},
62                                                    null_codepoint_set,
63                                                    reflexive_set,
64                                                    static_cast<const char *>(string_buffer),
65                                                    buffer_length,
66                                                    defined_cps);
67    }
68""")
69    cps = sorted(cp_value_map.keys())
70    string_buffer = ""
71    for cp in cps: 
72        string_buffer += cp_value_map[cp] + "\n"
73    buffer_length = len(string_buffer.encode("utf-8"))
74    f.write(s.substitute(prop_enum = property_code,
75    prop_enum_up = property_code.upper(),
76    string_buffer = string_buffer,
77    buffer_length = buffer_length,
78    allocation_length = (buffer_length + 255) & -256,
79    null_set_ranges = cformat.multiline_fill(['[%04x, %04x]' % (lo, hi) for (lo, hi) in uset_to_range_list(null_set)], ',', 8),
80    null_set_value = null_set.showC(12),
81    reflexive_set_ranges = cformat.multiline_fill(['[%04x, %04x]' % (lo, hi) for (lo, hi) in uset_to_range_list(reflexive_set)], ',', 8),
82    reflexive_set_value = reflexive_set.showC(12),
83    explicitly_defined_cp_count = len(cps),
84    explicitly_defined_cps = cformat.multiline_fill(['0x%04x' % cp for cp in cps], ',', 8)
85    ))
86
87def emit_numeric_property(f, property_code, NaN_set, cp_value_map):
88    s = string.Template(r"""    namespace ${prop_enum_up}_ns {
89        /** Code Point Ranges for ${prop_enum} mapping to NaN
90        ${NaN_set_ranges}**/
91
92        const UnicodeSet NaN_set
93        ${NaN_set_value};
94
95       const unsigned buffer_length = ${buffer_length};
96        const static char __attribute__ ((aligned (32))) string_buffer[${allocation_length}] = u8R"__(${string_buffer})__";
97
98        const static std::vector<codepoint_t> defined_cps = {
99        ${explicitly_defined_cps}};
100        static NumericPropertyObject property_object(${prop_enum},
101                                                    NaN_set,
102                                                    static_cast<const char *>(string_buffer),
103                                                    buffer_length,
104                                                    defined_cps);
105    }
106""")
107    cps = sorted(cp_value_map.keys())
108    string_buffer = ""
109    for cp in cps: 
110        string_buffer += cp_value_map[cp] + "\n"
111    buffer_length = len(string_buffer.encode("utf-8"))
112    f.write(s.substitute(prop_enum = property_code,
113    prop_enum_up = property_code.upper(),
114    string_buffer = string_buffer,
115    buffer_length = buffer_length,
116    allocation_length = (buffer_length + 255) & -256,
117    NaN_set_ranges = cformat.multiline_fill(['[%04x, %04x]' % (lo, hi) for (lo, hi) in uset_to_range_list(NaN_set)], ',', 8),
118    NaN_set_value = NaN_set.showC(12),
119    explicitly_defined_cp_count = len(cps),
120    explicitly_defined_cps = cformat.multiline_fill(['0x%04x' % cp for cp in cps], ',', 8)
121    ))
122
123
124def emit_binary_property(f, property_code, property_set):
125    f.write("    namespace %s_ns {\n" % property_code.upper())
126    f.write("        /** Code Point Ranges for %s\n        " % property_code)
127    f.write(cformat.multiline_fill(['[%04x, %04x]' % (lo, hi) for (lo, hi) in uset_to_range_list(property_set)], ',', 8))
128    f.write("**/\n")
129    f.write("        const UnicodeSet codepoint_set \n")
130    f.write(property_set.showC(12) + ";\n")
131    f.write("        static BinaryPropertyObject property_object{%s, codepoint_set};\n    }\n" % property_code)
132
133def emit_enumerated_property(f, property_code, independent_prop_values, prop_values, value_map):
134    f.write("  namespace %s_ns {\n" % property_code.upper())
135    f.write("    const unsigned independent_prop_values = %s;\n" % independent_prop_values)
136    for v in prop_values:
137        f.write("    /** Code Point Ranges for %s\n    " % v)
138        f.write(cformat.multiline_fill(['[%04x, %04x]' % (lo, hi) for (lo, hi) in uset_to_range_list(value_map[v])], ',', 4))
139        f.write("**/\n")
140        f.write("    const UnicodeSet %s_Set \n" % v.lower())
141        f.write(value_map[v].showC(8) + ";\n")
142    set_list = ['&%s_Set' % v.lower() for v in prop_values]
143    f.write("    static EnumeratedPropertyObject property_object\n")
144    f.write("        {%s,\n" % property_code)
145    f.write("         %s_ns::independent_prop_values,\n" % property_code.upper())
146    f.write("         %s_ns::enum_names,\n" % property_code.upper())
147    f.write("         %s_ns::value_names,\n" % property_code.upper())
148    f.write("         %s_ns::aliases_only_map,\n" % property_code.upper())
149    f.write("         {")
150    f.write(cformat.multiline_fill(set_list, ',', 8))
151    f.write("\n         }};\n    }\n")
152
153def emit_Obsolete_property(f, property_code):
154    s = string.Template(r"""    namespace ${prop_enum_up}_ns {
155        static ObsoletePropertyObject property_object(${prop_enum});
156    }
157""")
158    f.write(s.substitute(prop_enum = property_code, prop_enum_up = property_code.upper()))
159
160
161class UCD_generator():
162    def __init__(self):
163        self.supported_props = []
164        self.property_data_headers = []
165        self.missing_specs = {}
166        self.binary_properties = {}
167
168    def load_property_name_info(self):
169        (self.property_enum_name_list, self.property_object_map) = parse_PropertyAlias_txt()
170        self.property_lookup_map = getPropertyLookupMap(self.property_object_map)
171        self.full_name_map = {}
172        for p in self.property_enum_name_list:
173            self.full_name_map[p] = self.property_object_map[p].getPropertyFullName()
174
175
176    def generate_PropertyAliases_h(self):
177        f = cformat.open_header_file_for_write('PropertyAliases')
178        cformat.write_imports(f, ["<string>", "<unordered_map>", "<vector>"])
179        enum_text = cformat.multiline_fill(self.property_enum_name_list, ',', 8)
180        enum_text2 = cformat.multiline_fill(['"%s"' % e for e in self.property_enum_name_list], ',', 8)
181        full_name_text = cformat.multiline_fill(['"%s"' % self.full_name_map[e] for e in self.property_enum_name_list], ',', 8)
182        map_text = cformat.multiline_fill(['{"%s", %s}' % (k, self.property_lookup_map[k]) for k in sorted(self.property_lookup_map.keys())], ',', 8)
183        f.write(PropertyAliases_template % (enum_text, enum_text2, full_name_text, map_text))
184        cformat.close_header_file(f)
185
186    def load_property_value_info(self):
187        initializePropertyValues(self.property_object_map, self.property_lookup_map)
188
189    def generate_PropertyValueAliases_h(self):
190        f = cformat.open_header_file_for_write('PropertyValueAliases')
191        cformat.write_imports(f, ['"PropertyAliases.h"', "<vector>", "<unordered_map>", "<string>"])
192        f.write("namespace UCD {\n")
193        #  Generate the aliases for all Binary properties.
194        enum_text = cformat.multiline_fill(['N', 'Y'], ',', 12)
195        enum_names = cformat.multiline_fill(['"N"', '"Y"'], ',', 12)
196        full_name_text = cformat.multiline_fill(['"No"', '"Yes"'], ',', 12)
197        binary_properties = ['{"n", N}', '{"y", Y}', '{"no", N}', '{"yes", Y}', '{"f", N}', '{"t", Y}', '{"false", N}', '{"true", Y}']
198        binary_map_text = cformat.multiline_fill(binary_properties, ',', 12)
199        f.write(EnumeratedProperty_template % ('Binary', enum_text, enum_names, full_name_text, binary_map_text))
200        #
201        for p in self.property_enum_name_list:
202            po = self.property_object_map[p]
203            if isinstance(po, EnumeratedPropertyObject):
204                ordered_enum_list = po.property_value_list
205                enum_text = cformat.multiline_fill(ordered_enum_list, ',', 12)
206                enum_names = cformat.multiline_fill(['"%s"' % s for s in ordered_enum_list], ',', 12)
207                if p == 'ccc': # Special case: add numeric value information for ccc.
208                    enum_text += r"""
209        };
210        const uint16_t enum_val[] = {
211        """
212                    enum_text += "      " + cformat.multiline_fill(["%s" % (po.property_value_enum_integer[e]) for e in ordered_enum_list], ',', 12)
213                full_names = [po.property_value_full_name_map[e] for e in ordered_enum_list]
214                full_name_text = cformat.multiline_fill(['"%s"' % name for name in full_names], ',', 12)
215                canon_full_names = [canonicalize(name) for name in full_names]
216                canon_enums = [canonicalize(e) for e in ordered_enum_list]
217                canon_keys = [canonicalize(k) for k in po.property_value_lookup_map.keys()]
218                aliases_only = []
219                for k in canon_keys:
220                    if k in canon_enums: continue
221                    if k in canon_full_names: continue
222                    if k in aliases_only: continue
223                    aliases_only.append(k)
224                map_text = cformat.multiline_fill(['{"%s", %s_ns::%s}' % (k, p.upper(), po.property_value_lookup_map[k]) for k in sorted(aliases_only)], ',', 12)
225                f.write(EnumeratedProperty_template % (p.upper(), enum_text, enum_names, full_name_text, map_text))
226        f.write("}\n")
227        cformat.close_header_file(f)
228
229    def emit_property(self, f, property_code):
230        property_object = self.property_object_map[property_code]
231        if isinstance(property_object, BinaryPropertyObject):
232            emit_binary_property(f, property_code, property_object.value_map['Y'])
233            print("%s: %s bytes" % (property_object.getPropertyFullName(), property_object.value_map['Y'].bytes()))
234        elif isinstance(property_object, EnumeratedPropertyObject):
235            prop_values = property_object.name_list_order
236            independent_prop_values = property_object.independent_prop_values
237            emit_enumerated_property(f, property_code, independent_prop_values, prop_values, property_object.value_map)
238            print("%s: %s bytes" % (property_object.getPropertyFullName(), sum([property_object.value_map[v].bytes() for v in property_object.value_map.keys()])))
239        elif isinstance(property_object, StringPropertyObject):
240            emit_string_property(f, property_code, property_object.null_str_set, property_object.reflexive_set, property_object.cp_value_map)
241        elif isinstance(property_object, NumericPropertyObject):
242            emit_numeric_property(f, property_code, property_object.NaN_set, property_object.cp_value_map)
243        elif isinstance(property_object, ObsoletePropertyObject):
244            emit_Obsolete_property(f, property_code)
245        else: 
246            print("%s: unsupported property.")
247            return
248        self.supported_props.append(property_code)
249
250    def generate_property_value_file(self, filename_root, property_code):
251        property_object = self.property_object_map[property_code]
252        parse_property_data(self.property_object_map[property_code], filename_root + '.txt')
253        basename = os.path.basename(filename_root)
254        f = cformat.open_header_file_for_write(basename)
255        cformat.write_imports(f, ['"PropertyAliases.h"', '"PropertyObjects.h"', '"PropertyValueAliases.h"', '"unicode_set.h"'])
256        f.write("\nnamespace UCD {\n")
257        self.emit_property(f, property_code)
258        f.write("}\n")
259        cformat.close_header_file(f)
260        self.property_data_headers.append(basename)
261
262    def generate_multisection_properties_file(self, filename_root):
263        props = parse_multisection_property_data(filename_root + '.txt', self.property_object_map, self.property_lookup_map)
264        #(props, prop_map) = parse_UCD_codepoint_name_map(filename_root + '.txt', self.property_lookup_map)
265        basename = os.path.basename(filename_root)
266        f = cformat.open_header_file_for_write(basename)
267        cformat.write_imports(f, ['"PropertyAliases.h"', '"PropertyObjects.h"', '"PropertyValueAliases.h"', '"unicode_set.h"'])
268        f.write("\nnamespace UCD {\n")
269        for p in sorted(props):
270            self.emit_property(f, p)
271            property_object = self.property_object_map[p]
272        f.write("}\n\n")
273        cformat.close_header_file(f)
274        self.property_data_headers.append(basename)
275
276    def generate_multicolumn_properties_file(self, filename_root, prop_code_list):
277        props = parse_multicolumn_property_data(filename_root + '.txt', self.property_object_map, self.property_lookup_map, prop_code_list)
278        #(props, prop_map) = parse_UCD_codepoint_name_map(filename_root + '.txt', self.property_lookup_map)
279        basename = os.path.basename(filename_root)
280        f = cformat.open_header_file_for_write(basename)
281        cformat.write_imports(f, ['"PropertyAliases.h"', '"PropertyObjects.h"', '"PropertyValueAliases.h"', '"unicode_set.h"'])
282        f.write("\nnamespace UCD {\n")
283        for p in prop_code_list:
284            if p in self.property_object_map: self.emit_property(f, p)
285        f.write("}\n\n")
286        cformat.close_header_file(f)
287        self.property_data_headers.append(basename)
288
289    def generate_UnicodeData_h(self):
290        basename = 'UnicodeData'
291        parse_UnicodeData_txt(self.property_object_map)
292        f = cformat.open_header_file_for_write(basename)
293        cformat.write_imports(f, ['"PropertyAliases.h"', '"PropertyObjects.h"', '"PropertyValueAliases.h"', '"unicode_set.h"'])
294        prop_code_list = ['na', 'dm', 'suc', 'slc', 'stc', 'na1', 'isc', 'nv']
295        f.write("\nnamespace UCD {\n")
296        for p in prop_code_list:
297            self.emit_property(f, p)
298            property_object = self.property_object_map[p]
299        f.write("}\n\n")
300        cformat.close_header_file(f)
301        self.property_data_headers.append(basename)
302
303    def generate_SpecialCasing_h(self):
304        basename = 'SpecialCasing'
305        parse_SpecialCasing_txt(self.property_object_map)
306        f = cformat.open_header_file_for_write(basename)
307        cformat.write_imports(f, ['"PropertyAliases.h"', '"PropertyObjects.h"', '"PropertyValueAliases.h"', '"unicode_set.h"'])
308        f.write("\nnamespace UCD {\n")
309        for p in ['lc', 'uc', 'tc']:
310            self.emit_property(f, p)
311            property_object = self.property_object_map[p]
312        f.write("}\n\n")
313        cformat.close_header_file(f)
314        self.property_data_headers.append(basename)
315
316    def generate_ScriptExtensions_h(self):
317        filename_root = 'ScriptExtensions'
318        property_code = 'scx'
319        extension_object = self.property_object_map['scx']
320        extension_object.setBaseProperty(self.property_object_map['sc'])
321        parse_property_data(extension_object, filename_root+'.txt')
322        basename = os.path.basename(filename_root)
323        f = cformat.open_header_file_for_write(basename)
324        cformat.write_imports(f, ['"PropertyAliases.h"', '"PropertyObjects.h"', '"PropertyValueAliases.h"', '"unicode_set.h"'])
325        prop_list = self.property_object_map['sc'].name_list_order
326        value_map = extension_object.value_map
327        f.write("\nnamespace UCD {\n")
328        f.write("    namespace SCX_ns {\n")
329        for v in prop_list:
330            f.write("        /** Code Point Ranges for %s\n        " % v)
331            f.write(cformat.multiline_fill(['[%04x, %04x]' % (lo, hi) for (lo, hi) in uset_to_range_list(value_map[v])], ',', 8))
332            f.write("**/\n")
333            f.write("        const UnicodeSet %s_Ext \n" % v.lower())
334            f.write(value_map[v].showC(12) + ";\n")
335        set_list = ['&%s_Ext' % v.lower() for v in prop_list]
336        f.write("        static ExtensionPropertyObject property_object\n")
337        f.write("       {%s,\n" % property_code)
338        f.write("        UCD::sc,\n")
339        f.write("       {")
340        f.write(cformat.multiline_fill(set_list, ',', 8))
341        f.write("\n        }};\n    }\n}\n")
342        cformat.close_header_file(f)
343        print("%s: %s bytes" % (basename, sum([value_map[v].bytes() for v in value_map.keys()])))
344        self.supported_props.append(property_code)
345        self.property_data_headers.append(basename)
346
347    def generate_PropertyObjectTable_h(self):
348        f = cformat.open_header_file_for_write('PropertyObjectTable')
349        cformat.write_imports(f, ['"PropertyObjects.h"', '"PropertyAliases.h"', '<array>'])
350        cformat.write_imports(f, ['"%s.h"' % fname for fname in self.property_data_headers])
351        f.write("\nnamespace UCD {\n")
352        objlist = []
353        for p in self.property_enum_name_list:
354            k = self.property_object_map[p].getPropertyKind()
355            if p in self.supported_props:
356                objlist.append("&%s_ns::property_object" % p.upper())
357            else:
358                objlist.append("new UnsupportedPropertyObject(%s, PropertyObject::ClassTypeId::%sProperty)" % (p, k))
359        f.write("\n  const std::array<PropertyObject *, %i> property_object_table = {{\n    " % len(objlist))
360        f.write(",\n    ".join(objlist) + '  }};\n}\n')
361        cformat.close_header_file(f)
362
363    def generate_UCD_Config_h(self):
364        setVersionfromReadMe_txt()
365        f = cformat.open_header_file_for_write('UCD_Config')
366        f.write("\nnamespace UCD {\n")
367        f.write("   const std::string UnicodeVersion = \"%s\";\n" % UCD_config.version)
368        f.write("}\n")
369        cformat.close_header_file(f)
370
371
372
373def UCD_main():
374    ucd = UCD_generator()
375
376    # First parse all property names and their aliases
377    ucd.load_property_name_info()
378    #
379    # Generate the PropertyAliases.h file to define all the Unicode property_t enum
380    # and the basic property information.
381    ucd.generate_PropertyAliases_h()
382    #
383    # Next parse all property value names and their aliases.  Generate the data.
384    ucd.load_property_value_info()
385
386    ucd.generate_UnicodeData_h()
387   
388    ucd.generate_SpecialCasing_h()
389   
390    ucd.generate_multicolumn_properties_file('NameAliases', ['Name_Alias', 'Alias_Kind'])
391
392    #
393    # The Age property
394    ucd.generate_property_value_file('DerivedAge', 'age')
395    #
396    # The Block property
397    ucd.generate_property_value_file('Blocks', 'blk')
398   
399    # Scripts
400    ucd.generate_property_value_file('Scripts', 'sc')
401    #
402    # # Script Extensions
403    ucd.generate_ScriptExtensions_h()
404    # #
405    # General Category
406    ucd.generate_property_value_file('extracted/DerivedGeneralCategory', 'gc')
407   
408    # Binary properties from PropList.txt
409    ucd.generate_multisection_properties_file('PropList')
410   
411    # Binary properties from DerivedCoreProperties.txt
412    ucd.generate_multisection_properties_file('DerivedCoreProperties')
413    #
414    #
415    # LineBreak types
416    #ucd.generate_property_value_file('extracted/DerivedLineBreak', 'lb')
417    ucd.generate_property_value_file('LineBreak', 'lb')
418    #
419    # Grapheme Cluster Break property
420    ucd.generate_property_value_file('auxiliary/GraphemeBreakProperty', 'GCB')
421    #
422    # Sentence Break property
423    ucd.generate_property_value_file('auxiliary/SentenceBreakProperty', 'SB')
424    #
425    # Word Break property
426    ucd.generate_property_value_file('auxiliary/WordBreakProperty', 'WB')
427    #
428    # East Asian Width - can use either source
429    ucd.generate_property_value_file('EastAsianWidth', 'ea')
430    #ucd.generate_property_value_file('extracted/DerivedEastAsianWidth', 'ea')
431    #
432    # Hangul Syllable Type
433    ucd.generate_property_value_file('HangulSyllableType', 'hst')
434    #
435    ucd.generate_multisection_properties_file('extracted/DerivedBinaryProperties')
436    # #
437    # # Canonical_Combining_Class
438    ucd.generate_property_value_file('extracted/DerivedCombiningClass', 'ccc')
439    #
440    # Decomposition Type
441    ucd.generate_property_value_file('extracted/DerivedDecompositionType', 'dt')
442    #
443    # Joining Group and Type
444    ucd.generate_property_value_file('extracted/DerivedJoiningGroup', 'jg')
445    ucd.generate_property_value_file('extracted/DerivedJoiningType', 'jt')
446    #
447    # Numeric Type and Value
448    ucd.generate_property_value_file('extracted/DerivedNumericType', 'nt')
449    #ucd.generate_property_value_file('extracted/DerivedNumericValue', 'nv')
450    #
451    # Normalization properties.
452    ucd.generate_multisection_properties_file('DerivedNormalizationProps')
453    #
454    # Bidirectional properties
455    ucd.generate_property_value_file('extracted/DerivedBidiClass', 'bc')
456    ucd.generate_multicolumn_properties_file('BidiBrackets', ['bpb', 'bpt'])
457    ucd.generate_property_value_file('BidiMirroring', 'bmg')
458
459    # Indic properties
460    ucd.generate_property_value_file('IndicPositionalCategory', 'InPC')
461    ucd.generate_property_value_file('IndicSyllabicCategory', 'InSC')
462
463    ucd.generate_property_value_file('CompositionExclusions', 'CE')
464    #
465    ucd.generate_property_value_file('Jamo', 'JSN')
466    #
467    #
468    #
469    ucd.generate_PropertyValueAliases_h()
470
471    ucd.generate_PropertyObjectTable_h()
472
473    ucd.generate_UCD_Config_h()
474
475if __name__ == "__main__":
476  UCD_main()
Note: See TracBrowser for help on using the repository browser.