diff options
Diffstat (limited to 'unicode-segmentation/scripts/unicode.py')
-rwxr-xr-x | unicode-segmentation/scripts/unicode.py | 375 |
1 files changed, 0 insertions, 375 deletions
diff --git a/unicode-segmentation/scripts/unicode.py b/unicode-segmentation/scripts/unicode.py deleted file mode 100755 index 580b3c0..0000000 --- a/unicode-segmentation/scripts/unicode.py +++ /dev/null @@ -1,375 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2011-2015 The Rust Project Developers. See the COPYRIGHT -# file at the top-level directory of this distribution and at -# http://rust-lang.org/COPYRIGHT. -# -# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -# <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -# option. This file may not be copied, modified, or distributed -# except according to those terms. - -# This script uses the following Unicode tables: -# - DerivedCoreProperties.txt -# - auxiliary/GraphemeBreakProperty.txt -# - auxiliary/WordBreakProperty.txt -# - ReadMe.txt -# - UnicodeData.txt -# -# Since this should not require frequent updates, we just store this -# out-of-line and check the unicode.rs file into git. - -import fileinput, re, os, sys - -preamble = '''// Copyright 2012-2018 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// NOTE: The following code was generated by "scripts/unicode.py", do not edit directly - -#![allow(missing_docs, non_upper_case_globals, non_snake_case)] -''' - -# Mapping taken from Table 12 from: -# http://www.unicode.org/reports/tr44/#General_Category_Values -expanded_categories = { - 'Lu': ['LC', 'L'], 'Ll': ['LC', 'L'], 'Lt': ['LC', 'L'], - 'Lm': ['L'], 'Lo': ['L'], - 'Mn': ['M'], 'Mc': ['M'], 'Me': ['M'], - 'Nd': ['N'], 'Nl': ['N'], 'No': ['N'], - 'Pc': ['P'], 'Pd': ['P'], 'Ps': ['P'], 'Pe': ['P'], - 'Pi': ['P'], 'Pf': ['P'], 'Po': ['P'], - 'Sm': ['S'], 'Sc': ['S'], 'Sk': ['S'], 'So': ['S'], - 'Zs': ['Z'], 'Zl': ['Z'], 'Zp': ['Z'], - 'Cc': ['C'], 'Cf': ['C'], 'Cs': ['C'], 'Co': ['C'], 'Cn': ['C'], -} - -# these are the surrogate codepoints, which are not valid rust characters -surrogate_codepoints = (0xd800, 0xdfff) - -UNICODE_VERSION = (12, 0, 0) - -UNICODE_VERSION_NUMBER = "%s.%s.%s" %UNICODE_VERSION - -def is_surrogate(n): - return surrogate_codepoints[0] <= n <= surrogate_codepoints[1] - -def fetch(f): - if not os.path.exists(os.path.basename(f)): - if "emoji" in f: - os.system("curl -O https://www.unicode.org/Public/emoji/%s.%s/%s" - % (UNICODE_VERSION[0], UNICODE_VERSION[1], f)) - else: - os.system("curl -O http://www.unicode.org/Public/%s/ucd/%s" - % (UNICODE_VERSION_NUMBER, f)) - - if not os.path.exists(os.path.basename(f)): - sys.stderr.write("cannot load %s" % f) - exit(1) - -def load_gencats(f): - fetch(f) - gencats = {} - - udict = {}; - range_start = -1; - for line in fileinput.input(f): - data = line.split(';'); - if len(data) != 15: - continue - cp = int(data[0], 16); - if is_surrogate(cp): - continue - if range_start >= 0: - for i in range(range_start, cp): - udict[i] = data; - range_start = -1; - if data[1].endswith(", First>"): - range_start = cp; - continue; - udict[cp] = data; - - for code in udict: - [code_org, name, gencat, combine, bidi, - decomp, deci, digit, num, mirror, - old, iso, upcase, lowcase, titlecase ] = udict[code]; - - # place letter in categories as appropriate - for cat in [gencat, "Assigned"] + expanded_categories.get(gencat, []): - if cat not in gencats: - gencats[cat] = [] - gencats[cat].append(code) - - gencats = group_cats(gencats) - return gencats - -def group_cats(cats): - cats_out = {} - for cat in cats: - cats_out[cat] = group_cat(cats[cat]) - return cats_out - -def group_cat(cat): - cat_out = [] - letters = sorted(set(cat)) - cur_start = letters.pop(0) - cur_end = cur_start - for letter in letters: - assert letter > cur_end, \ - "cur_end: %s, letter: %s" % (hex(cur_end), hex(letter)) - if letter == cur_end + 1: - cur_end = letter - else: - cat_out.append((cur_start, cur_end)) - cur_start = cur_end = letter - cat_out.append((cur_start, cur_end)) - return cat_out - -def ungroup_cat(cat): - cat_out = [] - for (lo, hi) in cat: - while lo <= hi: - cat_out.append(lo) - lo += 1 - return cat_out - -def format_table_content(f, content, indent): - line = " "*indent - first = True - for chunk in content.split(","): - if len(line) + len(chunk) < 98: - if first: - line += chunk - else: - line += ", " + chunk - first = False - else: - f.write(line + ",\n") - line = " "*indent + chunk - f.write(line) - -def load_properties(f, interestingprops): - fetch(f) - props = {} - re1 = re.compile(r"^ *([0-9A-F]+) *; *(\w+)") - re2 = re.compile(r"^ *([0-9A-F]+)\.\.([0-9A-F]+) *; *(\w+)") - - for line in fileinput.input(os.path.basename(f)): - prop = None - d_lo = 0 - d_hi = 0 - m = re1.match(line) - if m: - d_lo = m.group(1) - d_hi = m.group(1) - prop = m.group(2) - else: - m = re2.match(line) - if m: - d_lo = m.group(1) - d_hi = m.group(2) - prop = m.group(3) - else: - continue - if interestingprops and prop not in interestingprops: - continue - d_lo = int(d_lo, 16) - d_hi = int(d_hi, 16) - if prop not in props: - props[prop] = [] - props[prop].append((d_lo, d_hi)) - - # optimize if possible - for prop in props: - props[prop] = group_cat(ungroup_cat(props[prop])) - - return props - -def escape_char(c): - return "'\\u{%x}'" % c - -def emit_table(f, name, t_data, t_type = "&'static [(char, char)]", is_pub=True, - pfun=lambda x: "(%s,%s)" % (escape_char(x[0]), escape_char(x[1])), is_const=True): - pub_string = "const" - if not is_const: - pub_string = "let" - if is_pub: - pub_string = "pub " + pub_string - f.write(" %s %s: %s = &[\n" % (pub_string, name, t_type)) - data = "" - first = True - for dat in t_data: - if not first: - data += "," - first = False - data += pfun(dat) - format_table_content(f, data, 8) - f.write("\n ];\n\n") - -def emit_util_mod(f): - f.write(""" -pub mod util { - #[inline] - pub fn bsearch_range_table(c: char, r: &'static [(char,char)]) -> bool { - use core::cmp::Ordering::{Equal, Less, Greater}; - r.binary_search_by(|&(lo,hi)| { - if lo <= c && c <= hi { Equal } - else if hi < c { Less } - else { Greater } - }).is_ok() - } - - #[inline] - fn is_alphabetic(c: char) -> bool { - match c { - 'a' ... 'z' | 'A' ... 'Z' => true, - c if c > '\x7f' => super::derived_property::Alphabetic(c), - _ => false, - } - } - - #[inline] - fn is_numeric(c: char) -> bool { - match c { - '0' ... '9' => true, - c if c > '\x7f' => super::general_category::N(c), - _ => false, - } - } - - #[inline] - pub fn is_alphanumeric(c: char) -> bool { - is_alphabetic(c) || is_numeric(c) - } -} - -""") - -def emit_property_module(f, mod, tbl, emit): - f.write("mod %s {\n" % mod) - for cat in sorted(emit): - emit_table(f, "%s_table" % cat, tbl[cat], is_pub=False) - f.write(" #[inline]\n") - f.write(" pub fn %s(c: char) -> bool {\n" % cat) - f.write(" super::util::bsearch_range_table(c, %s_table)\n" % cat) - f.write(" }\n\n") - f.write("}\n\n") - -def emit_break_module(f, break_table, break_cats, name): - Name = name.capitalize() - f.write("""pub mod %s { - use core::result::Result::{Ok, Err}; - - pub use self::%sCat::*; - - #[allow(non_camel_case_types)] - #[derive(Clone, Copy, PartialEq, Eq, Debug)] - pub enum %sCat { -""" % (name, Name, Name)) - - break_cats.append("Any") - break_cats.sort() - for cat in break_cats: - f.write((" %sC_" % Name[0]) + cat + ",\n") - f.write(""" } - - fn bsearch_range_value_table(c: char, r: &'static [(char, char, %sCat)]) -> %sCat { - use core::cmp::Ordering::{Equal, Less, Greater}; - match r.binary_search_by(|&(lo, hi, _)| { - if lo <= c && c <= hi { Equal } - else if hi < c { Less } - else { Greater } - }) { - Ok(idx) => { - let (_, _, cat) = r[idx]; - cat - } - Err(_) => %sC_Any - } - } - - pub fn %s_category(c: char) -> %sCat { - bsearch_range_value_table(c, %s_cat_table) - } - -""" % (Name, Name, Name[0], name, Name, name)) - - emit_table(f, "%s_cat_table" % name, break_table, "&'static [(char, char, %sCat)]" % Name, - pfun=lambda x: "(%s,%s,%sC_%s)" % (escape_char(x[0]), escape_char(x[1]), Name[0], x[2]), - is_pub=False, is_const=True) - f.write("}\n") - -if __name__ == "__main__": - r = "tables.rs" - if os.path.exists(r): - os.remove(r) - with open(r, "w") as rf: - # write the file's preamble - rf.write(preamble) - rf.write(""" -/// The version of [Unicode](http://www.unicode.org/) -/// that this version of unicode-segmentation is based on. -pub const UNICODE_VERSION: (u64, u64, u64) = (%s, %s, %s); -""" % UNICODE_VERSION) - - # download and parse all the data - gencats = load_gencats("UnicodeData.txt") - derived = load_properties("DerivedCoreProperties.txt", ["Alphabetic"]) - - emit_util_mod(rf) - for (name, cat, pfuns) in ("general_category", gencats, ["N"]), \ - ("derived_property", derived, ["Alphabetic"]): - emit_property_module(rf, name, cat, pfuns) - - ### grapheme cluster module - # from http://www.unicode.org/reports/tr29/#Grapheme_Cluster_Break_Property_Values - grapheme_cats = load_properties("auxiliary/GraphemeBreakProperty.txt", []) - - # Control - # Note: - # This category also includes Cs (surrogate codepoints), but Rust's `char`s are - # Unicode Scalar Values only, and surrogates are thus invalid `char`s. - # Thus, we have to remove Cs from the Control category - grapheme_cats["Control"] = group_cat(list( - set(ungroup_cat(grapheme_cats["Control"])) - - set(ungroup_cat([surrogate_codepoints])))) - - grapheme_table = [] - for cat in grapheme_cats: - grapheme_table.extend([(x, y, cat) for (x, y) in grapheme_cats[cat]]) - emoji_props = load_properties("emoji-data.txt", ["Extended_Pictographic"]) - grapheme_table.extend([(x, y, "Extended_Pictographic") for (x, y) in emoji_props["Extended_Pictographic"]]) - grapheme_table.sort(key=lambda w: w[0]) - last = -1 - for chars in grapheme_table: - if chars[0] <= last: - raise "Grapheme tables and Extended_Pictographic values overlap; need to store these separately!" - last = chars[1] - emit_break_module(rf, grapheme_table, list(grapheme_cats.keys()) + ["Extended_Pictographic"], "grapheme") - rf.write("\n") - - word_cats = load_properties("auxiliary/WordBreakProperty.txt", []) - word_table = [] - for cat in word_cats: - word_table.extend([(x, y, cat) for (x, y) in word_cats[cat]]) - word_table.sort(key=lambda w: w[0]) - emit_break_module(rf, word_table, list(word_cats.keys()), "word") - - # There are some emoji which are also ALetter, so this needs to be stored separately - # For efficiency, we could still merge the two tables and produce an ALetterEP state - emoji_table = [(x, y, "Extended_Pictographic") for (x, y) in emoji_props["Extended_Pictographic"]] - emit_break_module(rf, emoji_table, ["Extended_Pictographic"], "emoji") - - sentence_cats = load_properties("auxiliary/SentenceBreakProperty.txt", []) - sentence_table = [] - for cat in sentence_cats: - sentence_table.extend([(x, y, cat) for (x, y) in sentence_cats[cat]]) - sentence_table.sort(key=lambda w: w[0]) - emit_break_module(rf, sentence_table, list(sentence_cats.keys()), "sentence") |