Files
2026-07-13 13:33:03 +08:00

164 lines
5.1 KiB
Python

#!/usr/bin/env python3
"""
Generate unicode_data.hpp and unicode_data.cpp from Python's unicodedata module.
Usage: python gen_unicode_data.py [output_dir]
Default output_dir: ../../transformers/llm/engine/src/
"""
import sys
import os
import unicodedata
# Unicode General Category -> enum value mapping
CATEGORIES = [
"Cn", # 0 Not Assigned
"Lu", # 1 Uppercase Letter
"Ll", # 2 Lowercase Letter
"Lt", # 3 Titlecase Letter
"Lm", # 4 Modifier Letter
"Lo", # 5 Other Letter
"Mn", # 6 Nonspacing Mark
"Mc", # 7 Spacing Mark
"Me", # 8 Enclosing Mark
"Nd", # 9 Decimal Number
"Nl", # 10 Letter Number
"No", # 11 Other Number
"Pc", # 12 Connector Punctuation
"Pd", # 13 Dash Punctuation
"Ps", # 14 Open Punctuation
"Pe", # 15 Close Punctuation
"Pi", # 16 Initial Punctuation
"Pf", # 17 Final Punctuation
"Po", # 18 Other Punctuation
"Sm", # 19 Math Symbol
"Sc", # 20 Currency Symbol
"Sk", # 21 Modifier Symbol
"So", # 22 Other Symbol
"Zs", # 23 Space Separator
"Zl", # 24 Line Separator
"Zp", # 25 Paragraph Separator
"Cc", # 26 Control
"Cf", # 27 Format
"Cs", # 28 Surrogate
"Co", # 29 Private Use
]
CAT_TO_IDX = {c: i for i, c in enumerate(CATEGORIES)}
MAX_CP = 0x110000
def get_category_ranges():
"""Build run-length encoded category ranges."""
ranges = []
prev_cat = -1
range_start = 0
for cp in range(MAX_CP):
try:
cat = unicodedata.category(chr(cp))
except (ValueError, OverflowError):
cat = "Cn"
idx = CAT_TO_IDX.get(cat, 0)
if idx != prev_cat:
if prev_cat >= 0:
ranges.append((range_start, cp - 1, prev_cat))
range_start = cp
prev_cat = idx
if prev_cat >= 0:
ranges.append((range_start, MAX_CP - 1, prev_cat))
return ranges
def get_tolower_map():
"""Build toLower mapping (only entries where lower != original)."""
mapping = []
for cp in range(MAX_CP):
try:
ch = chr(cp)
lc = ch.lower()
if len(lc) == 1:
lcp = ord(lc)
if lcp != cp:
mapping.append((cp, lcp))
except (ValueError, OverflowError):
pass
return mapping
def write_hpp(out_dir):
path = os.path.join(out_dir, "unicode_data.hpp")
with open(path, 'w') as f:
f.write("// Auto-generated by gen_unicode_data.py - DO NOT EDIT\n")
f.write("#ifndef UNICODE_DATA_HPP\n")
f.write("#define UNICODE_DATA_HPP\n\n")
f.write("#include <cstdint>\n")
f.write("#include <cstddef>\n\n")
f.write("namespace MNN {\nnamespace Unicode {\n\n")
f.write("enum Category : uint8_t {\n")
for i, c in enumerate(CATEGORIES):
f.write(f" CAT_{c} = {i},\n")
f.write(f" CAT_COUNT = {len(CATEGORIES)}\n")
f.write("};\n\n")
f.write("struct CategoryRange {\n uint32_t start;\n uint32_t end;\n uint8_t category;\n};\n\n")
f.write("struct CaseMapping {\n uint32_t from;\n uint32_t to;\n};\n\n")
f.write("extern const CategoryRange kCategoryRanges[];\n")
f.write("extern const size_t kCategoryRangesSize;\n\n")
f.write("extern const CaseMapping kToLower[];\n")
f.write("extern const size_t kToLowerSize;\n\n")
f.write("} // namespace Unicode\n} // namespace MNN\n\n")
f.write("#endif // UNICODE_DATA_HPP\n")
print(f" wrote {path}")
def write_cpp(out_dir):
print(" generating category ranges...")
cat_ranges = get_category_ranges()
print(f" {len(cat_ranges)} ranges")
print(" generating toLower map...")
tolower = get_tolower_map()
print(f" {len(tolower)} entries")
path = os.path.join(out_dir, "unicode_data.cpp")
with open(path, 'w') as f:
f.write("// Auto-generated by gen_unicode_data.py - DO NOT EDIT\n")
f.write('#include "unicode_data.hpp"\n\n')
f.write("namespace MNN {\nnamespace Unicode {\n\n")
# Category ranges
f.write(f"const CategoryRange kCategoryRanges[] = {{\n")
for start, end, cat in cat_ranges:
f.write(f" {{0x{start:X}, 0x{end:X}, {cat}}},\n")
f.write("};\n")
f.write(f"const size_t kCategoryRangesSize = {len(cat_ranges)};\n\n")
# toLower
f.write(f"const CaseMapping kToLower[] = {{\n")
for frm, to in tolower:
f.write(f" {{0x{frm:X}, 0x{to:X}}},\n")
f.write("};\n")
f.write(f"const size_t kToLowerSize = {len(tolower)};\n\n")
f.write("} // namespace Unicode\n} // namespace MNN\n")
print(f" wrote {path}")
def main():
out_dir = sys.argv[1] if len(sys.argv) > 1 else os.path.join(os.path.dirname(__file__), "..", "..", "transformers", "llm", "engine", "src")
out_dir = os.path.abspath(out_dir)
os.makedirs(out_dir, exist_ok=True)
print(f"Output directory: {out_dir}")
write_hpp(out_dir)
write_cpp(out_dir)
print("Done.")
if __name__ == "__main__":
main()