ctok 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ctok/__init__.py +5 -0
- ctok/constants.py +224 -0
- ctok/data/pieces_v3.json +48722 -0
- ctok/data/pieces_v4_7.json +15315 -0
- ctok/engine.py +220 -0
- ctok/main.py +214 -0
- ctok/normalize.py +550 -0
- ctok/notation.py +87 -0
- ctok/witness.py +139 -0
- ctok-1.0.0.dist-info/METADATA +133 -0
- ctok-1.0.0.dist-info/RECORD +14 -0
- ctok-1.0.0.dist-info/WHEEL +4 -0
- ctok-1.0.0.dist-info/entry_points.txt +2 -0
- ctok-1.0.0.dist-info/licenses/LICENSE +21 -0
ctok/__init__.py
ADDED
ctok/constants.py
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
"""The marker notation and the measured character tables.
|
|
2
|
+
|
|
3
|
+
The tables were measured against ``count_tokens``. Tables marked as enumerated must stay
|
|
4
|
+
enumerated: no Unicode-category rule reproduces them.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import re
|
|
8
|
+
|
|
9
|
+
# ---- marker notation ----------------------------------------------------------------------------
|
|
10
|
+
|
|
11
|
+
# The engine computes over marked strings in which each structural marker is one codepoint.
|
|
12
|
+
BOW_G, EOW_G, PAD_G = "\ufdd0", "\ufdd1", "\ufdd2"
|
|
13
|
+
SHIFT_G, CAPS_G = "\ufdd3", "\ufdd4"
|
|
14
|
+
|
|
15
|
+
MARKER_GLYPHS = frozenset({BOW_G, EOW_G, SHIFT_G, CAPS_G})
|
|
16
|
+
|
|
17
|
+
# The public form: named atoms in mathematical angle brackets, practically absent from real text.
|
|
18
|
+
# Literal brackets in text are byte-escaped at render time, so a rendered token always parses back
|
|
19
|
+
# unambiguously.
|
|
20
|
+
L, R = "⟨", "⟩"
|
|
21
|
+
BOW, EOW, SHIFT, CAPS = f"{L}bow{R}", f"{L}eow{R}", f"{L}shift{R}", f"{L}caps{R}"
|
|
22
|
+
PAD = f"{L}pad{R}"
|
|
23
|
+
|
|
24
|
+
GLYPH_TO_ATOM = {BOW_G: BOW, EOW_G: EOW, PAD_G: PAD, SHIFT_G: SHIFT, CAPS_G: CAPS}
|
|
25
|
+
ATOM_TO_GLYPH = {BOW: BOW_G, EOW: EOW_G, SHIFT: SHIFT_G, CAPS: CAPS_G}
|
|
26
|
+
|
|
27
|
+
# Scraped text can contain the noncharacters used above. NFC strips BMP private-use input, so these
|
|
28
|
+
# five private-use codepoints are safe internal escapes inserted after NFC. The byte floor and
|
|
29
|
+
# renderer map them back to the original noncharacters.
|
|
30
|
+
LITERAL_MARKER_ESCAPES = {glyph: chr(0xE000 + i) for i, glyph in enumerate(GLYPH_TO_ATOM)}
|
|
31
|
+
ESCAPED_MARKER_LITERALS = {escape: glyph for glyph, escape in LITERAL_MARKER_ESCAPES.items()}
|
|
32
|
+
LITERAL_MARKER_ESCAPE_TABLE = str.maketrans(LITERAL_MARKER_ESCAPES)
|
|
33
|
+
|
|
34
|
+
# ---- character classes --------------------------------------------------------------------------
|
|
35
|
+
|
|
36
|
+
WORDY, HARD, DIGIT, PUNCT, SPACE = "wordy", "hard", "digit", "punct", "space"
|
|
37
|
+
|
|
38
|
+
# Non-ASCII symbols/punctuation that tile over the punct vocabulary rather than standing alone.
|
|
39
|
+
# Enumerated: the behaviour splits per codepoint with no categorical rule (`(` is punct but `)`
|
|
40
|
+
# is hard). Anything unlisted stays HARD.
|
|
41
|
+
PUNCT_SYMS = frozenset("—»«•°„–−£§€…√→(№†└│།·─═█")
|
|
42
|
+
|
|
43
|
+
# Symbol-letters measured to take the full word model (⟨bow⟩ and ⟨eow⟩ flanks, seam absorption, case
|
|
44
|
+
# markers) exactly like Latin letters. Enumerated blocks: category Nl/So/Lu splits both ways, and
|
|
45
|
+
# the Hangzhou numerals (also Nl) measured markerless, so it is the block that predicts, not the
|
|
46
|
+
# category.
|
|
47
|
+
SYMBOL_LETTERS = (
|
|
48
|
+
(0x16EE, 0x16F0), # Runic golden numbers (Nl, caseless)
|
|
49
|
+
(0x2160, 0x2188), # Roman numerals (Nl/Lu/Ll, cased pairs)
|
|
50
|
+
(0x24B6, 0x24E9), # circled letters (So, cased)
|
|
51
|
+
(0xA6E6, 0xA6EF), # Bamum number-letters (Nl, caseless)
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
# Variation selectors are gc=Mn but take no word model: each costs one more than its base
|
|
55
|
+
# character, where ⟨bow⟩ and ⟨eow⟩ flanks would read three more. The supplementary selectors
|
|
56
|
+
# (U+E0100 to U+E01EF) are astral and already HARD.
|
|
57
|
+
VARIATION_SELECTORS = (0xFE00, 0xFE0F)
|
|
58
|
+
|
|
59
|
+
# The one canonical-combining-class-9 character that does not separate word runs: measured over
|
|
60
|
+
# as a separator on its own script's consonants and under wherever no letter precedes it. See
|
|
61
|
+
# :func:`normalize.is_separator` for the grid.
|
|
62
|
+
NON_SEPARATORS = frozenset("ฺ") # THAI CHARACTER PHINTHU
|
|
63
|
+
|
|
64
|
+
# Marks that separate word runs the way a virama does, but without combining class 9. Measured
|
|
65
|
+
# one character at a time on byte-floor consonant hosts; enumerated, because the rule is
|
|
66
|
+
# orthographic (the neighbouring codepoint is usually a vowel sign that does not split). The
|
|
67
|
+
# U+0300 block is handled as a range instead. See :data:`SEPARATOR_MARKS`.
|
|
68
|
+
SEPARATOR_SIGNS = frozenset((
|
|
69
|
+
"\u0740", # SYRIAC FEMININE DOT
|
|
70
|
+
"\u0741", # SYRIAC QUSHSHAYA
|
|
71
|
+
"\u0742", # SYRIAC RUKKAKHA
|
|
72
|
+
"\u0743", # SYRIAC TWO VERTICAL DOTS ABOVE
|
|
73
|
+
"\u0744", # SYRIAC TWO VERTICAL DOTS BELOW
|
|
74
|
+
"\u0745", # SYRIAC THREE DOTS ABOVE
|
|
75
|
+
"\u0746", # SYRIAC THREE DOTS BELOW
|
|
76
|
+
"\u0747", # SYRIAC OBLIQUE LINE ABOVE
|
|
77
|
+
"\u0748", # SYRIAC OBLIQUE LINE BELOW
|
|
78
|
+
"\u0749", # SYRIAC MUSIC
|
|
79
|
+
"\u074a", # SYRIAC BARREKH
|
|
80
|
+
"\u093c", # DEVANAGARI SIGN NUKTA
|
|
81
|
+
"\u0951", # DEVANAGARI STRESS SIGN UDATTA
|
|
82
|
+
"\u0952", # DEVANAGARI STRESS SIGN ANUDATTA
|
|
83
|
+
"\u0953", # DEVANAGARI GRAVE ACCENT
|
|
84
|
+
"\u0954", # DEVANAGARI ACUTE ACCENT
|
|
85
|
+
"\u09bc", # BENGALI SIGN NUKTA
|
|
86
|
+
"\u09fe", # BENGALI SANDHI MARK
|
|
87
|
+
"\u0a3c", # GURMUKHI SIGN NUKTA
|
|
88
|
+
"\u0abc", # GUJARATI SIGN NUKTA
|
|
89
|
+
"\u0afd", # GUJARATI SIGN THREE-DOT NUKTA ABOVE
|
|
90
|
+
"\u0afe", # GUJARATI SIGN CIRCLE NUKTA ABOVE
|
|
91
|
+
"\u0aff", # GUJARATI SIGN TWO-CIRCLE NUKTA ABOVE
|
|
92
|
+
"\u0b3c", # ORIYA SIGN NUKTA
|
|
93
|
+
"\u0b55", # ORIYA SIGN OVERLINE
|
|
94
|
+
# U+0C03 TELUGU SIGN VISARGA is deliberately not here: it is a spacing mark (Mc, ccc 0) that
|
|
95
|
+
# measures as plain word material, closing a word with a `ః⟨eow⟩` piece rather than splitting it.
|
|
96
|
+
"\u0c3c", # TELUGU SIGN NUKTA
|
|
97
|
+
"\u0cbc", # KANNADA SIGN NUKTA
|
|
98
|
+
"\u0e47", # THAI CHARACTER MAITAIKHU
|
|
99
|
+
"\u0e48", # THAI CHARACTER MAI EK
|
|
100
|
+
"\u0e49", # THAI CHARACTER MAI THO
|
|
101
|
+
"\u0e4a", # THAI CHARACTER MAI TRI
|
|
102
|
+
"\u0e4b", # THAI CHARACTER MAI CHATTAWA
|
|
103
|
+
"\u0e4c", # THAI CHARACTER THANTHAKHAT
|
|
104
|
+
"\u0e4e", # THAI CHARACTER YAMAKKAN
|
|
105
|
+
"\u0ec8", # LAO TONE MAI EK
|
|
106
|
+
"\u0ec9", # LAO TONE MAI THO
|
|
107
|
+
"\u0eca", # LAO TONE MAI TI
|
|
108
|
+
"\u0ecb", # LAO TONE MAI CATAWA
|
|
109
|
+
"\u0ecc", # LAO CANCELLATION MARK
|
|
110
|
+
"\u0ece", # LAO YAMAKKAN
|
|
111
|
+
"\u0f18", # TIBETAN ASTROLOGICAL SIGN -KHYUD PA
|
|
112
|
+
"\u0f19", # TIBETAN ASTROLOGICAL SIGN SDONG TSHUGS
|
|
113
|
+
"\u0f35", # TIBETAN MARK NGAS BZUNG NYI ZLA
|
|
114
|
+
"\u0f37", # TIBETAN MARK NGAS BZUNG SGOR RTAGS
|
|
115
|
+
"\u0f39", # TIBETAN MARK TSA -PHRU
|
|
116
|
+
"\u0f3e", # TIBETAN SIGN YAR TSHES
|
|
117
|
+
"\u0f3f", # TIBETAN SIGN MAR TSHES
|
|
118
|
+
"\u0f86", # TIBETAN SIGN LCI RTAGS
|
|
119
|
+
"\u0f87", # TIBETAN SIGN YANG RTAGS
|
|
120
|
+
"\u0fc6", # TIBETAN SYMBOL PADMA GDAN
|
|
121
|
+
"\u1037", # MYANMAR SIGN DOT BELOW
|
|
122
|
+
"\u17b4", # KHMER VOWEL INHERENT AQ
|
|
123
|
+
"\u17b5", # KHMER VOWEL INHERENT AA
|
|
124
|
+
"\u17c9", # KHMER SIGN MUUSIKATOAN
|
|
125
|
+
"\u17ca", # KHMER SIGN TRIISAP
|
|
126
|
+
"\u17cb", # KHMER SIGN BANTOC
|
|
127
|
+
"\u17cc", # KHMER SIGN ROBAT
|
|
128
|
+
"\u17cd", # KHMER SIGN TOANDAKHIAT
|
|
129
|
+
"\u17ce", # KHMER SIGN KAKABAT
|
|
130
|
+
"\u17cf", # KHMER SIGN AHSDA
|
|
131
|
+
"\u17d0", # KHMER SIGN SAMYOK SANNYA
|
|
132
|
+
"\u17d1", # KHMER SIGN VIRIAM
|
|
133
|
+
"\u17d3", # KHMER SIGN BATHAMASAT
|
|
134
|
+
"\u17dd", # KHMER SIGN ATTHACAN
|
|
135
|
+
"\u1a75", # TAI THAM SIGN TONE-1
|
|
136
|
+
"\u1a76", # TAI THAM SIGN TONE-2
|
|
137
|
+
"\u1a77", # TAI THAM SIGN KHUEN TONE-3
|
|
138
|
+
"\u1a78", # TAI THAM SIGN KHUEN TONE-4
|
|
139
|
+
"\u1a79", # TAI THAM SIGN KHUEN TONE-5
|
|
140
|
+
"\u1a7a", # TAI THAM SIGN RA HAAM
|
|
141
|
+
"\u1a7b", # TAI THAM SIGN MAI SAM
|
|
142
|
+
"\u1a7c", # TAI THAM SIGN KHUEN-LUE KARAN
|
|
143
|
+
"\u1a7f", # TAI THAM COMBINING CRYPTOGRAMMIC DOT
|
|
144
|
+
"\u1b34", # BALINESE SIGN REREKAN
|
|
145
|
+
"\u1b6b", # BALINESE MUSICAL SYMBOL COMBINING TEGEH
|
|
146
|
+
"\u1b6c", # BALINESE MUSICAL SYMBOL COMBINING ENDEP
|
|
147
|
+
"\u1b6d", # BALINESE MUSICAL SYMBOL COMBINING KEMPUL
|
|
148
|
+
"\u1b6e", # BALINESE MUSICAL SYMBOL COMBINING KEMPLI
|
|
149
|
+
"\u1b6f", # BALINESE MUSICAL SYMBOL COMBINING JEGOGAN
|
|
150
|
+
"\u1b70", # BALINESE MUSICAL SYMBOL COMBINING KEMPUL WITH JEGOGAN
|
|
151
|
+
"\u1b71", # BALINESE MUSICAL SYMBOL COMBINING KEMPLI WITH JEGOGAN
|
|
152
|
+
"\u1b72", # BALINESE MUSICAL SYMBOL COMBINING BENDE
|
|
153
|
+
"\u1b73", # BALINESE MUSICAL SYMBOL COMBINING GONG
|
|
154
|
+
"\ua9b3", # JAVANESE SIGN CECAK TELU
|
|
155
|
+
))
|
|
156
|
+
|
|
157
|
+
# ---- normalization ------------------------------------------------------------------------------
|
|
158
|
+
|
|
159
|
+
# C0/C1 controls the API strips before tokenizing (cost 0), i.e. every gc=Cc except TAB, LF and NUL.
|
|
160
|
+
STRIP_CONTROL = re.compile("[\x01-\x08\x0B-\x1F\x7F\x80-\x9F]")
|
|
161
|
+
|
|
162
|
+
# BMP private use is stripped the same way, and its two neighbours join into one word. Unassigned
|
|
163
|
+
# codepoints survive and pay their bytes; the supplementary private-use planes are unprobed and
|
|
164
|
+
# deliberately left out.
|
|
165
|
+
STRIP_PRIVATE = re.compile("[-]")
|
|
166
|
+
|
|
167
|
+
# Lone surrogates are valid in a str but not UTF-8-encodable, so they would crash the byte floor.
|
|
168
|
+
# They can never be genuine tokenizer input; fold each to U+FFFD, which prices as an ordinary
|
|
169
|
+
# 3-byte HARD character. This is what keeps token_count total on any str.
|
|
170
|
+
SURROGATE = re.compile(r"[\ud800-\udfff]")
|
|
171
|
+
|
|
172
|
+
# Space separators the tokenizer treats identically to U+0020: all Zs except U+3000 (ideographic
|
|
173
|
+
# space), plus Zl/Zp. NB U+3000, TAB and LF each have their own cost. NUL is not stripped but
|
|
174
|
+
# tokenizes exactly like a space, so it is folded here too.
|
|
175
|
+
FUNNY_SPACE = re.compile("[\u00A0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F]")
|
|
176
|
+
|
|
177
|
+
# The four standard curly quotes fold to their ASCII forms (v3 only; NFC does not do this). The
|
|
178
|
+
# low-9 mark U+201E is a different token and is deliberately not folded.
|
|
179
|
+
QUOTE_FOLD = str.maketrans({"\u2018": "'", "\u2019": "'", "\u201C": '"', "\u201D": '"'})
|
|
180
|
+
|
|
181
|
+
# The suffixes an apostrophe binds into the word ahead of it, deleting that word's ⟨bow⟩. This is the
|
|
182
|
+
# standard English contraction set, lowercase and whole-word only. Measured per member; other
|
|
183
|
+
# suffixes pay a full boundary even when their own piece exists, so no piece-based rule reproduces
|
|
184
|
+
# this. Case-sensitive and whole-word (`x'S`, `x'LL`, `x'llo`, `x'lls` all pay).
|
|
185
|
+
CONTRACTION_SUFFIXES = frozenset({"s", "t", "d", "m", "ll", "re", "ve"})
|
|
186
|
+
|
|
187
|
+
# ---- the seam law -------------------------------------------------------------------------------
|
|
188
|
+
|
|
189
|
+
# ⟨eow⟩ ' ' [case markers] ⟨bow⟩ -> ⟨eow⟩ [case markers] ⟨bow⟩: a single space between two marked
|
|
190
|
+
# spans is the seam and is not written as a character.
|
|
191
|
+
SEAM_RE = re.compile("(.)" + EOW_G + " " + "([" + SHIFT_G + CAPS_G + "]*)" + BOW_G)
|
|
192
|
+
|
|
193
|
+
# ---- marks that stand outside the word -----------------------------------------------------------
|
|
194
|
+
|
|
195
|
+
# Combining marks that close the word before themselves, exactly as a virama does
|
|
196
|
+
# (`normalize.is_separator` folds this range in): `q́z` = `⟨bow⟩q⟨eow⟩` + mark + `⟨bow⟩z⟨eow⟩`, so an
|
|
197
|
+
# accent makes two words out of one. Two oracle frames on one-token hosts distinguish the
|
|
198
|
+
# spellings. A sweep covered U+0300 to U+036F on 21 hosts across 13 scripts and both families, with
|
|
199
|
+
# no dissent. U+0345 and the combining Latin letters U+0363 to U+036F pin both ends of the range
|
|
200
|
+
# from outside by reading "inside the word". U+0340, U+0341, U+0343, and U+0344 are NFC-folded away
|
|
201
|
+
# and inside the range for that reason alone.
|
|
202
|
+
SEPARATOR_MARKS = re.compile("[\u0300-\u0344\u0346-\u0362]")
|
|
203
|
+
|
|
204
|
+
# The same question asked of every other combining block of the BMP: 418 marks swept on the same
|
|
205
|
+
# two frames, both families, no dissent. The split is orthographic, as for Thai:
|
|
206
|
+
#
|
|
207
|
+
# OUTSIDE the word accents, tone marks, cantillation and annotation
|
|
208
|
+
# INSIDE the word vowel points and combining letters (niqqud, harakat, the Syriac vowels,
|
|
209
|
+
# Thaana, Samaritan, the combining Latin/Cyrillic letter blocks)
|
|
210
|
+
#
|
|
211
|
+
# Most ranges are pinned on both sides by an inside-the-word neighbour in the same block; where a
|
|
212
|
+
# range abuts unassigned codepoints, it is written to the marks actually probed. The N'Ko and
|
|
213
|
+
# Mandaic marks have no in-script one-token host and rest on `q` and `б` alone.
|
|
214
|
+
SEPARATOR_ANNOTATIONS = re.compile(
|
|
215
|
+
"[\u0483-\u0489\u0591-\u05af\u0658\u06df-\u06e0\u06ea-\u06ec"
|
|
216
|
+
"\u07eb-\u07f3\u07fd\u0859-\u085b\u0898-\u089f\u08ca-\u08d3\u08e0-\u08e1"
|
|
217
|
+
"\u0818-\u0819\u082d\u08ea-\u08ef\u135d-\u135f"
|
|
218
|
+
"\u180b-\u180d\u180f\u1939-\u193b"
|
|
219
|
+
"\u1ab0-\u1abe\u1ac1-\u1acb\u1cd0-\u1ce8\u1ced\u1cf4\u1cf8-\u1cf9"
|
|
220
|
+
"\u1be6\u1c37\u1cf7"
|
|
221
|
+
"\u1dc0-\u1dd2\u1df5-\u1dff\u20d0-\u20f0\ua66f-\ua672\ua67c-\ua67d"
|
|
222
|
+
"\u2cef-\u2cf1\u302a-\u302f\u3099-\u309a"
|
|
223
|
+
"\ua6f0-\ua6f1\ua8e0-\ua8f1\ua92b-\ua92d\uaabf\uaac1\uabec"
|
|
224
|
+
"\ufe20-\ufe2f]")
|