make_uts46_mapping_table.py 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. # Copyright 2013-2014 The rust-url developers.
  2. #
  3. # Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
  4. # http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
  5. # <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
  6. # option. This file may not be copied, modified, or distributed
  7. # except according to those terms.
  8. # Run as: python make_uts46_mapping_table.py IdnaMappingTable.txt > uts46_mapping_table.rs
  9. # You can get the latest idna table from
  10. # http://www.unicode.org/Public/idna/latest/IdnaMappingTable.txt
  11. import collections
  12. import itertools
  13. print('''\
  14. // Copyright 2013-2014 The rust-url developers.
  15. //
  16. // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
  17. // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
  18. // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
  19. // option. This file may not be copied, modified, or distributed
  20. // except according to those terms.
  21. // Generated by make_idna_table.py
  22. static TABLE: &'static [Range] = &[
  23. ''')
  24. txt = open("IdnaMappingTable.txt")
  25. def escape_char(c):
  26. return "\\u{%x}" % ord(c[0])
  27. def char(s):
  28. return unichr(int(s, 16))
  29. strtab = collections.OrderedDict()
  30. strtab_offset = 0
  31. def strtab_slice(s):
  32. global strtab, strtab_offset
  33. if s in strtab:
  34. return strtab[s]
  35. else:
  36. utf8_len = len(s.encode('utf8'))
  37. c = (strtab_offset, utf8_len)
  38. strtab[s] = c
  39. strtab_offset += utf8_len
  40. return c
  41. def rust_slice(s):
  42. start = s[0]
  43. length = s[1]
  44. start_lo = start & 0xff
  45. start_hi = start >> 8
  46. assert length <= 255
  47. assert start_hi <= 255
  48. return "(StringTableSlice { byte_start_lo: %d, byte_start_hi: %d, byte_len: %d })" % (start_lo, start_hi, length)
  49. ranges = []
  50. for line in txt:
  51. # remove comments
  52. line, _, _ = line.partition('#')
  53. # skip empty lines
  54. if len(line.strip()) == 0:
  55. continue
  56. fields = line.split(';')
  57. if fields[0].strip() == 'D800..DFFF':
  58. continue # Surrogates don't occur in Rust strings.
  59. first, _, last = fields[0].strip().partition('..')
  60. if not last:
  61. last = first
  62. mapping = fields[1].strip().replace('_', ' ').title().replace(' ', '')
  63. unicode_str = None
  64. if len(fields) > 2:
  65. if fields[2].strip():
  66. unicode_str = u''.join(char(c) for c in fields[2].strip().split(' '))
  67. elif mapping == "Deviation":
  68. unicode_str = u''
  69. ranges.append((first, last, mapping, unicode_str))
  70. def mergeable_key(r):
  71. mapping = r[2]
  72. # These types have associated data, so we should not merge them.
  73. if mapping in ('Mapped', 'Deviation', 'DisallowedStd3Mapped'):
  74. return r
  75. assert mapping in ('Valid', 'Ignored', 'Disallowed', 'DisallowedStd3Valid')
  76. return mapping
  77. grouped_ranges = itertools.groupby(ranges, key=mergeable_key)
  78. optimized_ranges = []
  79. for (k, g) in grouped_ranges:
  80. group = list(g)
  81. if len(group) == 1:
  82. optimized_ranges.append(group[0])
  83. continue
  84. # Assert that nothing in the group has an associated unicode string.
  85. for g in group:
  86. if len(g[3]) > 2:
  87. assert not g[3][2].strip()
  88. # Assert that consecutive members of the group don't leave gaps in
  89. # the codepoint space.
  90. a, b = itertools.tee(group)
  91. next(b, None)
  92. for (g1, g2) in itertools.izip(a, b):
  93. last_char = int(g1[1], 16)
  94. next_char = int(g2[0], 16)
  95. if last_char + 1 == next_char:
  96. continue
  97. # There's a gap where surrogates would appear, but we don't have to
  98. # worry about that gap, as surrogates never appear in Rust strings.
  99. # Assert we're seeing the surrogate case here.
  100. assert last_char == 0xd7ff
  101. assert next_char == 0xe000
  102. first = group[0][0]
  103. last = group[-1][1]
  104. mapping = group[0][2]
  105. unicode_str = group[0][3]
  106. optimized_ranges.append((first, last, mapping, unicode_str))
  107. for (first, last, mapping, unicode_str) in optimized_ranges:
  108. if unicode_str is not None:
  109. mapping += rust_slice(strtab_slice(unicode_str))
  110. print(" Range { from: '%s', to: '%s', mapping: %s }," % (escape_char(char(first)),
  111. escape_char(char(last)),
  112. mapping))
  113. print("];\n")
  114. def escape_str(s):
  115. return [escape_char(c) for c in s]
  116. print("static STRING_TABLE: &'static str = \"%s\";"
  117. % '\\\n '.join(itertools.chain(*[escape_str(s) for s in strtab.iterkeys()])))