#!/usr/bin/env python3 """Write aac-core's keyboard/compose_table.rs: every precomposed letter of the Latin, Greek and Cyrillic blocks whose canonical decomposition is one letter and one combining mark, as (letter, mark, composed) — the composition step of NFC for the letters a keyboard puts a mark on (edit::mark). Characters Unicode excludes from composition are left out, as NFC leaves them. usage: python3 tools/keyboard/build_compose.py (from aac/aac-rs) """ import sys import unicodedata from pathlib import Path OUT = Path(__file__).resolve().parents[2] / "crates/aac-core/src/keyboard/compose_table.rs" RANGES = [(0x00C0, 0x024F), (0x0370, 0x03FF), (0x0400, 0x04FF), (0x1E00, 0x1EFF), (0x1F00, 0x1FFF)] rows = [] for lo, hi in RANGES: for n in range(lo, hi + 1): c = chr(n) d = unicodedata.decomposition(c) if not d or d.startswith("<"): continue parts = [chr(int(x, 16)) for x in d.split()] if len(parts) != 2 or not unicodedata.combining(parts[1]): continue # A composition exclusion (or a singleton) does not survive NFC. if unicodedata.normalize("NFC", parts[0] + parts[1]) != c: continue rows.append((ord(parts[0]), ord(parts[1]), n)) rows.sort() body = "\n".join(f" (0x{a:04X}, 0x{m:04X}, 0x{c:04X})," for a, m, c in rows) OUT.write_text(f"""//! Generated by `tools/keyboard/build_compose.py` (Unicode {unicodedata.unidata_version}); do not edit by hand. //! //! Every precomposed Latin, Greek and Cyrillic letter that NFC composes from //! one letter and one combining mark: `(letter, mark, composed)`, sorted. //! Derived from the Unicode Character Database, Copyright © 1991-2026 //! Unicode, Inc., under the Unicode License v3 //! (https://www.unicode.org/license.txt). pub(super) const COMPOSE: [(u32, u32, u32); {len(rows)}] = [ {body} ]; """, encoding="utf-8") print(f"{len(rows)} compositions -> {OUT}", file=sys.stderr)