unicode.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. # unicode.py
  2. import sys
  3. from itertools import filterfalse
  4. from typing import List, Tuple, Union
  5. class _lazyclassproperty:
  6. def __init__(self, fn):
  7. self.fn = fn
  8. self.__doc__ = fn.__doc__
  9. self.__name__ = fn.__name__
  10. def __get__(self, obj, cls):
  11. if cls is None:
  12. cls = type(obj)
  13. if not hasattr(cls, "_intern") or any(
  14. cls._intern is getattr(superclass, "_intern", [])
  15. for superclass in cls.__mro__[1:]
  16. ):
  17. cls._intern = {}
  18. attrname = self.fn.__name__
  19. if attrname not in cls._intern:
  20. cls._intern[attrname] = self.fn(cls)
  21. return cls._intern[attrname]
  22. UnicodeRangeList = List[Union[Tuple[int, int], Tuple[int]]]
  23. class unicode_set:
  24. """
  25. A set of Unicode characters, for language-specific strings for
  26. ``alphas``, ``nums``, ``alphanums``, and ``printables``.
  27. A unicode_set is defined by a list of ranges in the Unicode character
  28. set, in a class attribute ``_ranges``. Ranges can be specified using
  29. 2-tuples or a 1-tuple, such as::
  30. _ranges = [
  31. (0x0020, 0x007e),
  32. (0x00a0, 0x00ff),
  33. (0x0100,),
  34. ]
  35. Ranges are left- and right-inclusive. A 1-tuple of (x,) is treated as (x, x).
  36. A unicode set can also be defined using multiple inheritance of other unicode sets::
  37. class CJK(Chinese, Japanese, Korean):
  38. pass
  39. """
  40. _ranges: UnicodeRangeList = []
  41. @_lazyclassproperty
  42. def _chars_for_ranges(cls):
  43. ret = []
  44. for cc in cls.__mro__:
  45. if cc is unicode_set:
  46. break
  47. for rr in getattr(cc, "_ranges", ()):
  48. ret.extend(range(rr[0], rr[-1] + 1))
  49. return [chr(c) for c in sorted(set(ret))]
  50. @_lazyclassproperty
  51. def printables(cls):
  52. """all non-whitespace characters in this range"""
  53. return "".join(filterfalse(str.isspace, cls._chars_for_ranges))
  54. @_lazyclassproperty
  55. def alphas(cls):
  56. """all alphabetic characters in this range"""
  57. return "".join(filter(str.isalpha, cls._chars_for_ranges))
  58. @_lazyclassproperty
  59. def nums(cls):
  60. """all numeric digit characters in this range"""
  61. return "".join(filter(str.isdigit, cls._chars_for_ranges))
  62. @_lazyclassproperty
  63. def alphanums(cls):
  64. """all alphanumeric characters in this range"""
  65. return cls.alphas + cls.nums
  66. @_lazyclassproperty
  67. def identchars(cls):
  68. """all characters in this range that are valid identifier characters, plus underscore '_'"""
  69. return "".join(
  70. sorted(
  71. set(
  72. "".join(filter(str.isidentifier, cls._chars_for_ranges))
  73. + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzªµº"
  74. + "ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿ"
  75. + "_"
  76. )
  77. )
  78. )
  79. @_lazyclassproperty
  80. def identbodychars(cls):
  81. """
  82. all characters in this range that are valid identifier body characters,
  83. plus the digits 0-9, and · (Unicode MIDDLE DOT)
  84. """
  85. return "".join(
  86. sorted(
  87. set(
  88. cls.identchars
  89. + "0123456789·"
  90. + "".join(
  91. [c for c in cls._chars_for_ranges if ("_" + c).isidentifier()]
  92. )
  93. )
  94. )
  95. )
  96. @_lazyclassproperty
  97. def identifier(cls):
  98. """
  99. a pyparsing Word expression for an identifier using this range's definitions for
  100. identchars and identbodychars
  101. """
  102. from pyparsing import Word
  103. return Word(cls.identchars, cls.identbodychars)
  104. class pyparsing_unicode(unicode_set):
  105. """
  106. A namespace class for defining common language unicode_sets.
  107. """
  108. # fmt: off
  109. # define ranges in language character sets
  110. _ranges: UnicodeRangeList = [
  111. (0x0020, sys.maxunicode),
  112. ]
  113. class BasicMultilingualPlane(unicode_set):
  114. """Unicode set for the Basic Multilingual Plane"""
  115. _ranges: UnicodeRangeList = [
  116. (0x0020, 0xFFFF),
  117. ]
  118. class Latin1(unicode_set):
  119. """Unicode set for Latin-1 Unicode Character Range"""
  120. _ranges: UnicodeRangeList = [
  121. (0x0020, 0x007E),
  122. (0x00A0, 0x00FF),
  123. ]
  124. class LatinA(unicode_set):
  125. """Unicode set for Latin-A Unicode Character Range"""
  126. _ranges: UnicodeRangeList = [
  127. (0x0100, 0x017F),
  128. ]
  129. class LatinB(unicode_set):
  130. """Unicode set for Latin-B Unicode Character Range"""
  131. _ranges: UnicodeRangeList = [
  132. (0x0180, 0x024F),
  133. ]
  134. class Greek(unicode_set):
  135. """Unicode set for Greek Unicode Character Ranges"""
  136. _ranges: UnicodeRangeList = [
  137. (0x0342, 0x0345),
  138. (0x0370, 0x0377),
  139. (0x037A, 0x037F),
  140. (0x0384, 0x038A),
  141. (0x038C,),
  142. (0x038E, 0x03A1),
  143. (0x03A3, 0x03E1),
  144. (0x03F0, 0x03FF),
  145. (0x1D26, 0x1D2A),
  146. (0x1D5E,),
  147. (0x1D60,),
  148. (0x1D66, 0x1D6A),
  149. (0x1F00, 0x1F15),
  150. (0x1F18, 0x1F1D),
  151. (0x1F20, 0x1F45),
  152. (0x1F48, 0x1F4D),
  153. (0x1F50, 0x1F57),
  154. (0x1F59,),
  155. (0x1F5B,),
  156. (0x1F5D,),
  157. (0x1F5F, 0x1F7D),
  158. (0x1F80, 0x1FB4),
  159. (0x1FB6, 0x1FC4),
  160. (0x1FC6, 0x1FD3),
  161. (0x1FD6, 0x1FDB),
  162. (0x1FDD, 0x1FEF),
  163. (0x1FF2, 0x1FF4),
  164. (0x1FF6, 0x1FFE),
  165. (0x2129,),
  166. (0x2719, 0x271A),
  167. (0xAB65,),
  168. (0x10140, 0x1018D),
  169. (0x101A0,),
  170. (0x1D200, 0x1D245),
  171. (0x1F7A1, 0x1F7A7),
  172. ]
  173. class Cyrillic(unicode_set):
  174. """Unicode set for Cyrillic Unicode Character Range"""
  175. _ranges: UnicodeRangeList = [
  176. (0x0400, 0x052F),
  177. (0x1C80, 0x1C88),
  178. (0x1D2B,),
  179. (0x1D78,),
  180. (0x2DE0, 0x2DFF),
  181. (0xA640, 0xA672),
  182. (0xA674, 0xA69F),
  183. (0xFE2E, 0xFE2F),
  184. ]
  185. class Chinese(unicode_set):
  186. """Unicode set for Chinese Unicode Character Range"""
  187. _ranges: UnicodeRangeList = [
  188. (0x2E80, 0x2E99),
  189. (0x2E9B, 0x2EF3),
  190. (0x31C0, 0x31E3),
  191. (0x3400, 0x4DB5),
  192. (0x4E00, 0x9FEF),
  193. (0xA700, 0xA707),
  194. (0xF900, 0xFA6D),
  195. (0xFA70, 0xFAD9),
  196. (0x16FE2, 0x16FE3),
  197. (0x1F210, 0x1F212),
  198. (0x1F214, 0x1F23B),
  199. (0x1F240, 0x1F248),
  200. (0x20000, 0x2A6D6),
  201. (0x2A700, 0x2B734),
  202. (0x2B740, 0x2B81D),
  203. (0x2B820, 0x2CEA1),
  204. (0x2CEB0, 0x2EBE0),
  205. (0x2F800, 0x2FA1D),
  206. ]
  207. class Japanese(unicode_set):
  208. """Unicode set for Japanese Unicode Character Range, combining Kanji, Hiragana, and Katakana ranges"""
  209. class Kanji(unicode_set):
  210. "Unicode set for Kanji Unicode Character Range"
  211. _ranges: UnicodeRangeList = [
  212. (0x4E00, 0x9FBF),
  213. (0x3000, 0x303F),
  214. ]
  215. class Hiragana(unicode_set):
  216. """Unicode set for Hiragana Unicode Character Range"""
  217. _ranges: UnicodeRangeList = [
  218. (0x3041, 0x3096),
  219. (0x3099, 0x30A0),
  220. (0x30FC,),
  221. (0xFF70,),
  222. (0x1B001,),
  223. (0x1B150, 0x1B152),
  224. (0x1F200,),
  225. ]
  226. class Katakana(unicode_set):
  227. """Unicode set for Katakana Unicode Character Range"""
  228. _ranges: UnicodeRangeList = [
  229. (0x3099, 0x309C),
  230. (0x30A0, 0x30FF),
  231. (0x31F0, 0x31FF),
  232. (0x32D0, 0x32FE),
  233. (0xFF65, 0xFF9F),
  234. (0x1B000,),
  235. (0x1B164, 0x1B167),
  236. (0x1F201, 0x1F202),
  237. (0x1F213,),
  238. ]
  239. 漢字 = Kanji
  240. カタカナ = Katakana
  241. ひらがな = Hiragana
  242. _ranges = (
  243. Kanji._ranges
  244. + Hiragana._ranges
  245. + Katakana._ranges
  246. )
  247. class Hangul(unicode_set):
  248. """Unicode set for Hangul (Korean) Unicode Character Range"""
  249. _ranges: UnicodeRangeList = [
  250. (0x1100, 0x11FF),
  251. (0x302E, 0x302F),
  252. (0x3131, 0x318E),
  253. (0x3200, 0x321C),
  254. (0x3260, 0x327B),
  255. (0x327E,),
  256. (0xA960, 0xA97C),
  257. (0xAC00, 0xD7A3),
  258. (0xD7B0, 0xD7C6),
  259. (0xD7CB, 0xD7FB),
  260. (0xFFA0, 0xFFBE),
  261. (0xFFC2, 0xFFC7),
  262. (0xFFCA, 0xFFCF),
  263. (0xFFD2, 0xFFD7),
  264. (0xFFDA, 0xFFDC),
  265. ]
  266. Korean = Hangul
  267. class CJK(Chinese, Japanese, Hangul):
  268. """Unicode set for combined Chinese, Japanese, and Korean (CJK) Unicode Character Range"""
  269. class Thai(unicode_set):
  270. """Unicode set for Thai Unicode Character Range"""
  271. _ranges: UnicodeRangeList = [
  272. (0x0E01, 0x0E3A),
  273. (0x0E3F, 0x0E5B)
  274. ]
  275. class Arabic(unicode_set):
  276. """Unicode set for Arabic Unicode Character Range"""
  277. _ranges: UnicodeRangeList = [
  278. (0x0600, 0x061B),
  279. (0x061E, 0x06FF),
  280. (0x0700, 0x077F),
  281. ]
  282. class Hebrew(unicode_set):
  283. """Unicode set for Hebrew Unicode Character Range"""
  284. _ranges: UnicodeRangeList = [
  285. (0x0591, 0x05C7),
  286. (0x05D0, 0x05EA),
  287. (0x05EF, 0x05F4),
  288. (0xFB1D, 0xFB36),
  289. (0xFB38, 0xFB3C),
  290. (0xFB3E,),
  291. (0xFB40, 0xFB41),
  292. (0xFB43, 0xFB44),
  293. (0xFB46, 0xFB4F),
  294. ]
  295. class Devanagari(unicode_set):
  296. """Unicode set for Devanagari Unicode Character Range"""
  297. _ranges: UnicodeRangeList = [
  298. (0x0900, 0x097F),
  299. (0xA8E0, 0xA8FF)
  300. ]
  301. BMP = BasicMultilingualPlane
  302. # add language identifiers using language Unicode
  303. العربية = Arabic
  304. 中文 = Chinese
  305. кириллица = Cyrillic
  306. Ελληνικά = Greek
  307. עִברִית = Hebrew
  308. 日本語 = Japanese
  309. 한국어 = Korean
  310. ไทย = Thai
  311. देवनागरी = Devanagari
  312. # fmt: on