source: icGREP/icgrep-devel/UCD-scripts/UCD_property_objects.py

Last change on this file was 6192, checked in by cameron, 8 months ago

Updates for Unicode 11.0 plus Emoji properties

File size: 11.8 KB
Line 
1from unicode_set import *
2trivial_name_char_re = re.compile('[-_\s]')
3def canonicalize(property_string):
4    return trivial_name_char_re.sub('', property_string.lower())
5
6#
7#  Union of a list of sets
8#
9def union_of_all(uset_list):
10    if uset_list == []: return empty_uset()
11    else:
12        accum_set = uset_list[0]
13        for s in uset_list[1:]:
14            accum_set = uset_union(accum_set, s)
15        return accum_set
16
17
18class PropertyObject():
19    def __init__(self):
20        self.default_value = None
21    def setID(self, prop_code, long_name):
22        self.property_code = prop_code
23        self.full_name = long_name
24    def setAliases(self, aliases):
25        self.aliases = aliases
26    def setSource(self, file_root):
27        self.source_file = file_root
28    def setDefaultValue(self, default):
29        if self.default_value != None and self.default_value != default:
30            raise Exception("Conflicting default specification")
31        self.default_value = default
32    def addDataRecord(self, cp_lo, cp_hi, v):
33        pass
34    def finalizeProperty(self):
35        pass
36    def getPropertyCode(self):
37        return self.property_code
38    def getPropertyFullName(self):
39        return self.full_name
40    def getAliases(self):
41        return self.aliases
42    def getFileSource(self):
43        return self.source_file
44    def getAllCanonicalNames(self):
45        return [canonicalize(self.property_code), canonicalize(self.full_name)] + [canonicalize(x) for x in self.aliases]
46
47class EnumeratedPropertyObject(PropertyObject):
48    def __init__(self):
49        PropertyObject.__init__(self)
50        self.property_value_list = []
51        self.property_value_enum_integer = {}
52        self.property_value_full_name_map = {}
53        self.property_value_lookup_map = {}
54        self.enum_integer = 0
55        self.value_map = {}
56        self.default_value = None
57        self.name_list_order = []
58        self.independent_prop_values = 0
59
60    def getPropertyKind(self): return "Enumerated"
61
62    def addPropertyValue(self, value_enum, value_preferred_full_name, aliases):
63        if value_enum in self.property_value_list: raise Exception("Duplicate entry " + value_enum)
64        self.property_value_list.append(value_enum)
65        if self.property_code == "ccc":
66            self.property_value_enum_integer[value_enum] = int(aliases[0])
67        else:
68            self.property_value_enum_integer[value_enum] = self.enum_integer
69            self.enum_integer += 1
70        self.property_value_full_name_map[value_enum] = value_preferred_full_name
71        for name in [value_enum, value_preferred_full_name] + aliases:
72            self.property_value_lookup_map[name] = value_enum
73            self.property_value_lookup_map[canonicalize(name)] = value_enum
74        self.value_map[value_enum] = empty_uset()
75
76    def setDefaultValue(self, default):
77        if not default in self.property_value_lookup_map: 
78            raise Exception("Erroneous default value %s for property %s" % (default, self.full_name))
79        dflt = self.property_value_lookup_map[default]
80        if not dflt in self.name_list_order: self.name_list_order = [dflt] + self.name_list_order
81        PropertyObject.setDefaultValue(self, dflt)
82
83    def finalizeProperty(self):
84        explicitly_defined_cps = empty_uset()
85        for k in self.value_map.keys(): 
86            explicitly_defined_cps = uset_union(explicitly_defined_cps, self.value_map[k])
87        need_default_value = uset_complement(explicitly_defined_cps)
88        dflt = self.default_value
89        if not dflt == None:
90            if dflt in self.value_map:
91                self.value_map[dflt] = uset_union(self.value_map[dflt], need_default_value)
92            else: 
93                self.value_map[dflt] = need_default_value
94        self.independent_prop_values = len(self.name_list_order)
95        for v in self.property_value_list:
96            if not v in self.name_list_order:
97                self.name_list_order.append(v)
98        self.property_value_list = self.name_list_order
99        if self.property_code == 'gc':
100            # special logic for derived categories
101            self.value_map['LC'] = union_of_all([self.value_map[v] for v in ['Lu', 'Ll', 'Lt']])
102            self.value_map['L'] = union_of_all([self.value_map[v] for v in ['Lu', 'Ll', 'Lt', 'Lm', 'Lo']])
103            self.value_map['M'] = union_of_all([self.value_map[v] for v in ['Mn', 'Mc', 'Me']])
104            self.value_map['N'] = union_of_all([self.value_map[v] for v in ['Nd', 'Nl', 'No']])
105            self.value_map['P'] = union_of_all([self.value_map[v] for v in ['Pc', 'Pd', 'Ps', 'Pe', 'Pi', 'Pf', 'Po']])
106            self.value_map['S'] = union_of_all([self.value_map[v] for v in ['Sm', 'Sc', 'Sk', 'So']])
107            self.value_map['Z'] = union_of_all([self.value_map[v] for v in ['Zs', 'Zl', 'Zp']])
108            self.value_map['C'] = union_of_all([self.value_map[v] for v in ['Cc', 'Cf', 'Cs', 'Co', 'Cn']])
109
110
111
112    def addDataRecord(self, cp_lo, cp_hi, v):
113        canon = canonicalize(v)
114        if not canon in self.property_value_lookup_map: 
115            raise Exception("Unknown enumeration value for %s: %s" % (self.full_name, v))
116        enum_code = self.property_value_lookup_map[canon]
117        self.value_map[enum_code] = uset_union(self.value_map[enum_code], range_uset(cp_lo, cp_hi))
118        if not enum_code in self.name_list_order: self.name_list_order.append(enum_code)
119
120class BinaryPropertyObject(PropertyObject):
121    def __init__(self):
122        PropertyObject.__init__(self)
123        self.empty_regexp = re.compile("\s+")
124        self.property_value_full_name_map = {"N" : "No", "Y" : "Yes"}
125        self.name_list_order = ['N', 'Y']
126        self.property_value_lookup_map = {"n" : "N", "N" : "N", "no" : "N", "f" : "N", "false" : "N", 
127        "y" : "Y", "Y" : "Y", "yes" : "Y", "t" : "Y", "true" : "Y"}
128        self.default_value = "N"
129        self.value_map = {"N" : empty_uset(), "Y" : empty_uset()}
130
131    def getPropertyKind(self): return "Binary"
132
133    def addDataRecord(self, cp_lo, cp_hi, v):
134        if v==None or v in self.property_value_lookup_map[v] == 'Y':
135            self.value_map['Y'] = uset_union(self.value_map['Y'], range_uset(cp_lo, cp_hi))
136        else: 
137            self.value_map['Y'] = uset_difference(self.value_map['Y'], range_uset(cp_lo, cp_hi))
138
139    def setDefaultValue(self, default):
140        dflt = canonicalize(default)
141        if not dflt in self.property_value_lookup_map: 
142            raise Exception("Erroneous default value %s for property %s" % (default, self.full_name))
143        dflt = self.property_value_lookup_map[dflt]
144        if dflt != "N": 
145            raise Exception("Binary properties must have default value No")
146
147class NumericPropertyObject(PropertyObject):
148    def __init__(self):
149        PropertyObject.__init__(self)
150        self.cp_value_map = {}
151        self.NaN_set = empty_uset()
152
153    def getPropertyKind(self): return "Numeric"
154
155    def addDataRecord(self, cp_lo, cp_hi, stringValue):
156        if stringValue == '':
157            self.NaN_set = uset_union(self.NaN_set, range_uset(cp_lo, cp_hi))
158        else:
159            for cp in range(cp_lo, cp_hi+1):
160                self.cp_value_map[cp] = stringValue
161
162    def finalizeProperty(self):
163        explicitly_defined_cps = empty_uset()
164        for cp in self.cp_value_map.keys():
165            explicitly_defined_cps = uset_union(explicitly_defined_cps, singleton_uset(cp))
166        # set NaN default
167        self.NaN_set = uset_union(self.NaN_set, uset_complement(explicitly_defined_cps))
168
169
170class ExtensionPropertyObject(PropertyObject):
171    def __init__(self):
172        PropertyObject.__init__(self)
173        self.value_map = {}
174
175    def setBaseProperty(self, property_obj):
176        self.base_property = property_obj
177        for p in property_obj.property_value_list:
178            self.value_map[p] = empty_uset()
179
180    def getPropertyKind(self): return "Extension"
181
182    def addDataRecord(self, cp_lo, cp_hi, base_item_list):
183        newset = range_uset(cp_lo, cp_hi)
184        base_items = base_item_list.split()
185        for e in base_items:
186            self.value_map[e] = uset_union(self.value_map[e], newset)
187   
188    def finalizeProperty(self):
189        explicitly_defined_cps = empty_uset()
190        for k in self.value_map.keys(): 
191            explicitly_defined_cps = uset_union(explicitly_defined_cps, self.value_map[k])
192        # set <script> default
193        for k in self.base_property.value_map.keys():
194            base_set = self.base_property.value_map[k]
195            if k in ['Zzzz', 'Zyyy', 'Zinh']: # Unknown, Common, Inherited not included if the set is explicitly defined UAX #24
196                self.value_map[k] = uset_union(self.value_map[k], uset_difference(base_set, explicitly_defined_cps))
197            else:
198                self.value_map[k] = uset_union(self.value_map[k], base_set)
199
200codepoint_String_regexp = re.compile("^[A-F0-9]{4,6}(?: [A-F0-9]{4,6})*$")
201class StringPropertyObject(PropertyObject):
202    def __init__(self):
203        PropertyObject.__init__(self)
204        self.cp_value_map = {}
205        self.null_str_set = empty_uset()
206        self.reflexive_set = empty_uset()
207       
208    def getPropertyKind(self): 
209        return "String"
210
211    def addDataRecord(self, cp_lo, cp_hi, stringValue):
212        if stringValue == '':
213            self.null_str_set = uset_union(self.null_str_set, range_uset(cp_lo, cp_hi))
214        else:
215            if codepoint_String_regexp.match(stringValue):
216                s = ""
217                for cp in [int(x, 16) for x in stringValue.split(' ')]:
218                    s += chr(cp)
219                stringValue = s
220            for cp in range(cp_lo, cp_hi+1):
221                if len(stringValue) == 1 and ord(stringValue[0]) == cp:
222                    #print("Found reflexive entry for %s: %s" % (self.property_code, stringValue))
223                    self.reflexive_set = uset_union(self.reflexive_set, singleton_uset(ord(stringValue[0])))
224                else:
225                    self.cp_value_map[cp] = stringValue
226
227    def finalizeProperty(self):
228        explicitly_defined_cps = empty_uset()
229        for cp in self.cp_value_map.keys():
230            explicitly_defined_cps = uset_union(explicitly_defined_cps, singleton_uset(cp))
231        # set <script> default
232        if self.default_value == "<code point>":
233            self.reflexive_set = uset_union(self.reflexive_set, uset_complement(uset_union(explicitly_defined_cps, self.null_str_set)))
234        else:
235            self.null_str_set = uset_union(self.null_str_set, uset_complement(uset_union(explicitly_defined_cps, self.reflexive_set)))
236
237class StringOverridePropertyObject(PropertyObject):
238    def __init__(self, overridden_code):
239        PropertyObject.__init__(self)
240        self.cp_value_map = {}
241        self.overridden_code = overridden_code
242        self.overridden_set = empty_uset()
243       
244    def getPropertyKind(self): 
245        return "StringOverride"
246
247    def addDataRecord(self, cp_lo, cp_hi, stringValue):
248        if codepoint_String_regexp.match(stringValue):
249            s = ""
250            for cp in [int(x, 16) for x in stringValue.split(' ')]:
251                s += chr(cp)
252            stringValue = s
253        else: 
254            raise Exception("Expecting codepoint string, but got " + stringValue)
255        for cp in range(cp_lo, cp_hi+1): self.cp_value_map[cp] = stringValue
256
257    def finalizeProperty(self):
258        explicitly_defined_cps = empty_uset()
259        for cp in self.cp_value_map.keys():
260            explicitly_defined_cps = uset_union(explicitly_defined_cps, singleton_uset(cp))
261        self.overridden_set = explicitly_defined_cps
262
263class ObsoletePropertyObject(PropertyObject):
264    def __init__(self):
265        PropertyObject.__init__(self)
266
267    def getPropertyKind(self): return "Obsolete"
268
269
270def getPropertyLookupMap(property_object_map):
271    property_lookup_map = {}
272    for k in property_object_map.keys():
273        po = property_object_map[k]
274        names = po.getAllCanonicalNames()
275        for n in names:
276            property_lookup_map[n] = k
277    return property_lookup_map
278
Note: See TracBrowser for help on using the repository browser.