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

Last change on this file since 5666 was 5666, checked in by cameron, 23 months ago

Jamo Short Name support

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