unicode.py 10 KB

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