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

Last change on this file since 4155 was 4155, checked in by cameron, 5 years ago

Generate PropertyValueAliases?.h

File size: 10.0 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# September 10, 2014
8#
9# Licensed under Open Software License 3.0.
10#
11#
12import re, string
13from unicode_set import *
14
15UCD_dir = "7.0.0"
16
17
18#
19#  Processing files of the UCD
20#
21#  General format for skippable comments, blank lines
22UCD_skip = re.compile("^#.*$|^\s*$")
23
24#
25#  UCD Property File Format 1: property aliases
26#  PropertyAliases.txt
27#
28UCD_property_alias_regexp = re.compile("^([-A-Za-z_0-9]+)\s*;\s*([-A-Za-z_0-9]+)([^#]*)")
29
30def parse_PropertyAlias_txt():
31   property_enum_name_list = []
32   full_name_map = {}
33   property_lookup_map = {}
34   f = open(UCD_dir + "/" + 'PropertyAliases.txt')
35   lines = f.readlines()
36   for t in lines:
37      if UCD_skip.match(t): continue  # skip comment and blank lines
38      m = UCD_property_alias_regexp.match(t)
39      if not m: raise Exception("Unknown property alias syntax: %s" % t)
40      prop_enum = m.group(1)
41      prop_preferred_full_name = m.group(2)
42      prop_extra = m.group(3)
43      prop_aliases = re.findall("[-A-Za-z_0-9]+", prop_extra)
44      property_enum_name_list.append(prop_enum)
45      full_name_map[prop_enum] = prop_preferred_full_name
46      property_lookup_map[canonicalize(prop_enum)] = prop_enum
47      property_lookup_map[canonicalize(prop_preferred_full_name)] = prop_enum
48      for a in prop_aliases: property_lookup_map[canonicalize(a)] = prop_enum
49   return (property_enum_name_list, full_name_map, property_lookup_map)
50
51trivial_name_char_re = re.compile('[-_\s]')
52def canonicalize(property_string):
53   c = trivial_name_char_re.sub('', property_string.lower())
54   if len(c) > 2 and c[0:2] == "is": return c[2:]
55   else: return c
56
57
58PropertyAliases_template = r"""
59namespace UCD {
60  enum property_t {
61%s
62  };
63  const std::string[] property_full_name = {
64%s
65  };
66  const std::unordered_map<std::string, property> alias_map = {
67%s
68  };
69}
70"""
71
72def multiline_join(item_list, items_per_line, separator = ",", closer='', indent = 4):
73  lines = ""
74  sep_with_space = separator + " "
75  while len(item_list) > items_per_line:
76    line_items = item_list[:items_per_line]
77    lines += (" " * indent) + sep_with_space.join(line_items) + separator + "\n"
78    item_list = item_list[items_per_line:]
79  lines += (" " * indent) + sep_with_space.join(item_list) + closer
80  return lines
81
82enums_per_line = 4
83def generate_PropertyAliases_h():
84   (property_enum_name_list, full_name_map, property_lookup_map) = parse_PropertyAlias_txt()
85   f = open('PropertyAliases.h', 'w')
86   enum_text = multiline_join(property_enum_name_list, enums_per_line, ',')
87   full_name_text = multiline_join(['"%s"' % full_name_map[e] for e in property_enum_name_list], 2, ',')
88   map_text = multiline_join(['{"%s", %s}' % (k, property_lookup_map[k]) for k in sorted(property_lookup_map.keys())], 2, ',')
89   f.write(PropertyAliases_template % (enum_text, full_name_text, map_text))
90   f.close()
91   
92#
93#  UCD Property File Format 2: property value aliases
94#  PropertyValueAliases.txt
95#
96#  This file records value aliases for property values for
97#  each enumerated property, with the following additional notes:
98#  (1) The corresponding integer value of the enum constant is
99#      also specified for ccc (second field).
100#  (2) The Age property is a numeric type which has decimal float
101#      values as the enum constants: these won't be legal in enum syntax.
102#  (3) Binary properties also have enumerated values and aliases listed,
103#      although this is redundant, because all binary properties have the
104#      same value space.
105#
106
107UCD_property_value_alias_regexp = re.compile("^([-A-Za-z_0-9.]+)\s*;\s*([-A-Za-z_0-9.]+)\s*;\s*([-A-Za-z_0-9.]+)([^#]*)")
108
109def parse_PropertyValueAlias_txt():
110    property_value_list = {}
111    property_value_enum_integer = {}
112    property_value_full_name_map = {}
113    property_value_lookup_map = {}
114    f = open(UCD_dir + "/" + 'PropertyValueAliases.txt')
115    lines = f.readlines()
116    for t in lines:
117        if UCD_skip.match(t): continue  # skip comment and blank lines
118        m = UCD_property_value_alias_regexp.match(t)
119        if not m: raise Exception("Unknown property value alias syntax: %s" % t)
120        prop_code = m.group(1)
121        if not property_value_list.has_key(prop_code):
122          property_value_list[prop_code] = []
123          property_value_enum_integer[prop_code] = {}
124          property_value_full_name_map[prop_code] = {}
125          property_value_lookup_map[prop_code] = {}
126          enum_integer = 0
127        # Special case for ccc: second field is enum integer value
128        if prop_code == 'ccc':
129          enum_integer = int(m.group(2))
130          value_enum = m.group(3)
131          extra = m.group(4)
132          extra_list = re.findall("[-A-Za-z_0-9.]+", extra)
133          value_preferred_full_name = extra_list[0]
134          value_aliases = extra_list[1:]
135        # Special case for age: second field is numeric, third field is enum
136        # treat numeric value as an alias string
137        elif prop_code == 'age':
138          value_enum = m.group(3)
139          value_preferred_full_name = m.group(3)
140          extra = m.group(4)
141          value_aliases = [m.group(2)] + re.findall("[-A-Za-z_0-9]+", extra)
142        else:
143          value_enum = m.group(2)
144          value_preferred_full_name = m.group(3)
145          extra = m.group(4)
146          value_aliases = re.findall("[-A-Za-z_0-9]+", extra)
147        property_value_list[prop_code].append(value_enum)
148        property_value_enum_integer[prop_code][value_enum] = enum_integer
149        enum_integer += 1
150        property_value_full_name_map[prop_code][value_enum] = value_preferred_full_name
151        property_value_lookup_map[prop_code][canonicalize(value_enum)] = value_enum
152        property_value_lookup_map[prop_code][canonicalize(value_preferred_full_name)] = value_enum
153        for a in value_aliases: property_value_lookup_map[prop_code][canonicalize(a)] = value_enum
154    return (property_value_list, property_value_enum_integer, property_value_full_name_map, property_value_lookup_map)
155
156
157PropertyValueAliases_template = r"""
158namespace UCD {
159  enum %s_value_t {
160%s
161  };
162  const std::string[] %s_value_name = {
163%s
164  };
165  const std::unordered_map<std::string, property> %s_alias_map = {
166%s
167  };
168}
169"""
170
171def generate_PropertyValueAliases_h():
172   (property_enum_name_list, full_name_map, property_lookup_map) = parse_PropertyAlias_txt()
173   (property_value_list, property_value_enum_integer, property_value_full_name_map, property_value_lookup_map) = parse_PropertyValueAlias_txt()
174   f = open('PropertyValueAliases.h', 'w')
175   for p in property_enum_name_list:
176     if property_value_list.has_key(p):
177       if property_value_list[p] == ['N', 'Y']: continue # skip boolean properties
178       if p == 'ccc':
179         enum_text = multiline_join(["%s = %s" % (e, property_value_enum_integer[p][e]) for e in property_value_list['ccc']], 4, ',')
180       else: enum_text = multiline_join(property_value_list[p], 4, ',')
181       full_name_text = multiline_join(['"%s"' % (property_value_full_name_map[p][e]) for e in property_value_list[p]], 2, ',')
182       map_text = multiline_join(['{"%s", %s}' % (k, property_value_lookup_map[p][k]) for k in sorted(property_value_lookup_map[p].keys())], 2, ',')
183       f.write(PropertyValueAliases_template % (p, enum_text, p, full_name_text, p, map_text))
184   f.close()
185   
186
187
188#
189#  UCD Property File Format 3:  codepoint -> name maps
190#
191UCD_skip = re.compile("^#.*$|^\s*$")
192UCD_point_name_regexp = re.compile("^([0-9A-F]{4,6})\s*;\s*((?:[-A-Za-z0-9_]+\s+)*[-A-Za-z0-9_]+)\s*(?:[;#]|$)")
193UCD_range_name_regexp = re.compile("^([0-9A-F]{4,6})[.][.]([0-9A-F]{4,6})\s*;\s*((?:[-A-Za-z0-9_]+\s+)*[-A-Za-z0-9_]+)\s*(?:[;#]|$)")
194
195def parse_UCD_codepoint_name_map(mapfile):
196   name_map = {}
197   name_list_order = []
198   f = open(UCD_dir + "/" + mapfile)
199   lines = f.readlines()
200   for t in lines:
201      if UCD_skip.match(t): continue  # skip comment and blank lines
202      m = UCD_point_name_regexp.match(t)
203      if m:
204        (codepoint, name) = (int(m.group(1), 16), m.group(2))
205        newset = singleton_set(codepoint)
206      else: 
207        m = UCD_range_name_regexp.match(t)
208        if not m: raise Exception("Unknown syntax: %s" % t)
209        (cp_lo, cp_hi, name) = (int(m.group(1), 16), int(m.group(2), 16), m.group(3))
210        newset = make_range_set(cp_lo, cp_hi)
211      if not name_map.has_key(name):
212        name_map[name] = newset
213        name_list_order.append(name)
214      else: name_map[name] = union(name_map[name], newset)
215   return (name_list_order, name_map)
216
217def generate_PropList_h():
218   (props, prop_map) = parse_UCD_codepoint_name_map('PropList.txt')
219   f = open('PropList.h', 'w')
220   for k in props:
221     f.write(prop_map[k].showC(k))
222   f.close()
223
224def generate_Blocks_h():
225   (blocks, block_map) = parse_UCD_codepoint_name_map('Blocks.txt')
226   f = open('Blocks.h', 'w')
227   for k in blocks:
228     f.write(block_map[k].showC('block["%s"]' % k))
229   f.close()
230
231def generate_Scripts_h():
232   (scripts, script_map) = parse_UCD_codepoint_name_map('Scripts.txt')
233   f = open('Scripts.h', 'w')
234   for k in scripts:
235     f.write(script_map[k].showC('script["%s"]' % k))
236   f.close()
237   
238def generate_ScriptExtensions_h():
239   (scx_sets, scx_map) = parse_UCD_codepoint_name_map('ScriptExtensions.txt')
240   map2 = {}
241   f = open('ScriptExtensions.h', 'w')
242   for scx_list in scx_sets:
243     scx_items = scx_list.split(" ")
244     for scx in scx_items:
245        if map2.has_key(scx): 
246           map2[scx] = union(map2[scx], scx_map[scx_list])
247        else: map2[scx] = scx_map[scx_list]
248   for k in sorted(map2.keys()):
249     f.write(map2[k].showC('scx["%s"]' % k))
250   f.close()
251
252def generate_DerivedGeneralCategory_h():
253   (categories, cat_map) = parse_UCD_codepoint_name_map('extracted/DerivedGeneralCategory.txt')
254   f = open('DerivedGeneralCategory.h', 'w')
255   for k in categories:
256     f.write(cat_map[k].showC('GC["%s"]' % k))
257   f.close()
258
259def generate_DerivedCoreProperties_h():
260   (properties, prop_map) = parse_UCD_codepoint_name_map('DerivedCoreProperties.txt')
261   f = open('DerivedCoreProperties.h', 'w')
262   for k in properties:
263     f.write(prop_map[k].showC(k))
264   f.close()
265
266
Note: See TracBrowser for help on using the repository browser.