#!/usr/bin/env python3 """Write hanja.dict from result-per-row UTF-8 sources.""" import sys import unicodedata from pathlib import Path SOURCES = [Path(__file__).with_name(name) for name in ("hanja.src", "mssymbol.src")] MAXCANDIDATES = 128 def ishangul(s): return s != "" and all(0xAC00 <= ord(c) <= 0xD7A3 for c in s) def isjamo(s): return len(s) == 1 and 0x3131 <= ord(s) <= 0x314E def ishanja(s): return s != "" and all(0x3400 <= ord(c) <= 0x4DBF or 0x4E00 <= ord(c) <= 0x9FFF or 0xF900 <= ord(c) <= 0xFAFF for c in s) def issymbol(s): """One rune the popup can draw and a candidate row can carry: not a space, which the row separates candidates with, and not a formatting character, which would leave a blank candidate to pick.""" return (len(s) == 1 and not s.isspace() and not ishanja(s) and not unicodedata.category(s).startswith("C")) def read(path, table, seen): """Adds path's rows to table, keyed by reading, and returns its header.""" comments = [] leading = True found = False with path.open(encoding="utf-8") as src: for lineno, raw in enumerate(src, 1): line = raw.rstrip("\r\n") if not line: continue if line.startswith("#"): if leading: comments.append(";;" + line[1:]) continue leading = False fields = line.split("\t") if len(fields) != 2: raise ValueError(f"{path}:{lineno}: need resultreading") value, reading = fields if ishangul(reading): if not ishanja(value): raise ValueError(f"{path}:{lineno}: need BMP Hanja") elif isjamo(reading): if not issymbol(value): raise ValueError(f"{path}:{lineno}: need one symbol rune") else: raise ValueError(f"{path}:{lineno}: need a syllable or jamo " "reading") pair = (value, reading) if pair in seen: raise ValueError(f"{path}:{lineno}: duplicate reading") seen.add(pair) found = True table.setdefault(reading, []).append(value) if not found: raise ValueError(f"{path}: no readings") return comments def main(): sys.stdout.reconfigure(encoding="utf-8") sys.stderr.reconfigure(encoding="utf-8") paths = [Path(arg) for arg in sys.argv[1:]] or SOURCES table = {} seen = set() try: comments = [line for path in paths for line in read(path, table, seen)] for line in comments: print(line) if comments: print() for reading, candidates in table.items(): print(f"{reading}\t{' '.join(candidates[:MAXCANDIDATES])}") except (OSError, UnicodeError, ValueError) as error: print(error, file=sys.stderr) return 1 return 0 if __name__ == "__main__": sys.exit(main())