aboutsummaryrefslogtreecommitdiffstats
path: root/text/unicode.py
diff options
context:
space:
mode:
Diffstat (limited to 'text/unicode.py')
-rw-r--r--text/unicode.py88
1 files changed, 88 insertions, 0 deletions
diff --git a/text/unicode.py b/text/unicode.py
index ac0abaf..e4710db 100644
--- a/text/unicode.py
+++ b/text/unicode.py
@@ -74,9 +74,51 @@ def two_stage(name, values):
return '\n'.join(out), len(stage1) * 2 + len(blocks) * 256
+def unicode_data():
+ """Each code point's UnicodeData.txt fields (ranges expanded)."""
+ rows, start = {}, None
+ for line in (UCD / 'UnicodeData.txt').read_text(encoding='utf-8').splitlines():
+ fields = line.split(';')
+ code = int(fields[0], 16)
+ if fields[1].endswith(', First>'):
+ start = code
+ continue
+ if fields[1].endswith(', Last>'):
+ for c in range(start, code + 1):
+ rows[c] = fields
+ continue
+ rows[code] = fields
+ return rows
+
+
+def normalization():
+ """Canonical combining classes, full canonical decompositions (Hangul
+ syllables aside: they are algorithmic) and the primary composites."""
+ rows = unicode_data()
+ ccc = [0] * (LAST + 1)
+ single = {}
+ for code, fields in rows.items():
+ ccc[code] = int(fields[3])
+ if fields[5] and not fields[5].startswith('<'):
+ single[code] = [int(x, 16) for x in fields[5].split()]
+
+ def full(code):
+ return [c for d in single[code] for c in (full(d) if d in single else [d])]
+
+ excluded = set()
+ for first, last, fields, missing in ranges('DerivedNormalizationProps.txt'):
+ if not missing and fields[0] == 'Full_Composition_Exclusion':
+ excluded.update(range(first, last + 1))
+ decompositions = {code: full(code) for code in single}
+ compositions = sorted((d[0], d[1], code) for code, d in single.items() if len(d) == 2 and code not in excluded)
+ return ccc, decompositions, compositions
+
+
def main():
bidi = derived('extracted/DerivedBidiClass.txt', BIDI, LONG)
table, size = two_stage('bidi', bidi)
+ ccc, decompositions, compositions = normalization()
+ ccc_table, ccc_size = two_stage('ccc', ccc)
brackets = []
for first, _, fields, missing in ranges('BidiBrackets.txt'):
if not missing:
@@ -128,6 +170,52 @@ uint32_t vega_mirror(uint32_t code) {
long row = find(&mirrors[0][0], sizeof(mirrors) / sizeof(*mirrors), 2, code);
return row < 0 ? 0 : mirrors[row][1];
}''')
+ print()
+ print(f'/* Canonical_Combining_Class ({ccc_size} bytes). */')
+ print(ccc_table)
+ print()
+ print('uint8_t vega_combining_class(uint32_t code) {')
+ print(' return code > 0x10FFFF ? 0 : ccc_values[ccc_blocks[code >> 8] * 256u + (code & 255)];')
+ print('}')
+ pool, rows = [], []
+ for code in sorted(decompositions):
+ rows.append((code, len(pool), len(decompositions[code])))
+ pool.extend(decompositions[code])
+ print()
+ print('/* Full canonical decompositions (Hangul syllables aside): code, start in the pool, length. */')
+ print(f'static const uint32_t decompositions[{len(rows)}][3] = {{')
+ for code, start, length in rows:
+ print(f' {{0x{code:04X}, {start}, {length}}},')
+ print('};')
+ print(f'static const uint32_t decomposed[{len(pool)}] = {{')
+ for i in range(0, len(pool), 12):
+ print(' ' + ', '.join(f'0x{c:04X}' for c in pool[i:i + 12]) + ',')
+ print('};')
+ print()
+ print('size_t vega_decomposition(uint32_t code, const uint32_t **out) {')
+ print(' long row = find(&decompositions[0][0], sizeof(decompositions) / sizeof(*decompositions), 3, code);')
+ print(' if (row < 0) return 0;')
+ print(' *out = decomposed + decompositions[row][1];')
+ print(' return decompositions[row][2];')
+ print('}')
+ print()
+ print('/* The primary composites: first, second, composite, by first then second. */')
+ print(f'static const uint32_t compositions[{len(compositions)}][3] = {{')
+ for first, second, code in compositions:
+ print(f' {{0x{first:04X}, 0x{second:04X}, 0x{code:04X}}},')
+ print('};')
+ print('''
+uint32_t vega_composition(uint32_t first, uint32_t second) {
+ size_t low = 0, high = sizeof(compositions) / sizeof(*compositions);
+ while (low < high) {
+ size_t middle = (low + high) / 2;
+ const uint32_t *row = compositions[middle];
+ if (row[0] < first || (row[0] == first && row[1] < second)) low = middle + 1;
+ else high = middle;
+ }
+ return low < sizeof(compositions) / sizeof(*compositions) && compositions[low][0] == first &&
+ compositions[low][1] == second ? compositions[low][2] : 0;
+}''')
if __name__ == '__main__':