Skip to content

Commit 47a8c76

Browse files
committed
auto merge of #12561 : pzol/rust/char-case, r=alexcrichton
Added common and simple case folding, i.e. mapping one to one character mapping. For more information see http://www.unicode.org/faq/casemap_charprop.html Removed auto-generated dead code which wasn't used.
2 parents 3fbee34 + dba5625 commit 47a8c76

File tree

3 files changed

+1194
-1427
lines changed

3 files changed

+1194
-1427
lines changed

src/etc/unicode.py

+79-39
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
# programs". It is not meant to be a complete implementation of unicode.
2020
# For that we recommend you use a proper binding to libicu.
2121

22-
import fileinput, re, os, sys
22+
import fileinput, re, os, sys, operator
2323

2424

2525
def fetch(f):
@@ -35,6 +35,8 @@ def fetch(f):
3535
def load_unicode_data(f):
3636
fetch(f)
3737
gencats = {}
38+
upperlower = {}
39+
lowerupper = {}
3840
combines = []
3941
canon_decomp = {}
4042
compat_decomp = {}
@@ -44,6 +46,7 @@ def load_unicode_data(f):
4446
c_hi = 0
4547
com_lo = 0
4648
com_hi = 0
49+
4750
for line in fileinput.input(f):
4851
fields = line.split(";")
4952
if len(fields) != 15:
@@ -52,7 +55,17 @@ def load_unicode_data(f):
5255
decomp, deci, digit, num, mirror,
5356
old, iso, upcase, lowcase, titlecase ] = fields
5457

55-
code = int(code, 16)
58+
code_org = code
59+
code = int(code, 16)
60+
61+
# generate char to char direct common and simple conversions
62+
# uppercase to lowercase
63+
if gencat == "Lu" and lowcase != "" and code_org != lowcase:
64+
upperlower[code] = int(lowcase, 16)
65+
66+
# lowercase to uppercase
67+
if gencat == "Ll" and upcase != "" and code_org != upcase:
68+
lowerupper[code] = int(upcase, 16)
5669

5770
if decomp != "":
5871
if decomp.startswith('<'):
@@ -96,7 +109,7 @@ def load_unicode_data(f):
96109
com_lo = code
97110
com_hi = code
98111

99-
return (canon_decomp, compat_decomp, gencats, combines)
112+
return (canon_decomp, compat_decomp, gencats, combines, lowerupper, upperlower)
100113

101114
def load_properties(f, interestingprops):
102115
fetch(f)
@@ -147,25 +160,28 @@ def ch_prefix(ix):
147160

148161
def emit_bsearch_range_table(f):
149162
f.write("""
150-
fn bsearch_range_table(c: char, r: &'static [(char,char)]) -> bool {
151-
use cmp::{Equal, Less, Greater};
152-
use vec::ImmutableVector;
153-
use option::None;
154-
r.bsearch(|&(lo,hi)| {
155-
if lo <= c && c <= hi { Equal }
156-
else if hi < c { Less }
157-
else { Greater }
158-
}) != None
159-
}\n\n
163+
fn bsearch_range_table(c: char, r: &'static [(char,char)]) -> bool {
164+
use cmp::{Equal, Less, Greater};
165+
use vec::ImmutableVector;
166+
use option::None;
167+
r.bsearch(|&(lo,hi)| {
168+
if lo <= c && c <= hi { Equal }
169+
else if hi < c { Less }
170+
else { Greater }
171+
}) != None
172+
}\n\n
160173
""");
161174

162175
def emit_property_module(f, mod, tbl):
163176
f.write("pub mod %s {\n" % mod)
164177
keys = tbl.keys()
165178
keys.sort()
166-
emit_bsearch_range_table(f);
179+
167180
for cat in keys:
168-
if cat == "Cs": continue
181+
if cat not in ["Nd", "Nl", "No", "Cc",
182+
"XID_Start", "XID_Continue", "Alphabetic",
183+
"Lowercase", "Uppercase", "White_Space"]:
184+
continue
169185
f.write(" static %s_table : &'static [(char,char)] = &[\n" % cat)
170186
ix = 0
171187
for pair in tbl[cat]:
@@ -175,35 +191,55 @@ def emit_property_module(f, mod, tbl):
175191
f.write("\n ];\n\n")
176192

177193
f.write(" pub fn %s(c: char) -> bool {\n" % cat)
178-
f.write(" bsearch_range_table(c, %s_table)\n" % cat)
194+
f.write(" super::bsearch_range_table(c, %s_table)\n" % cat)
179195
f.write(" }\n\n")
180196
f.write("}\n")
181197

182198

183-
def emit_property_module_old(f, mod, tbl):
184-
f.write("mod %s {\n" % mod)
185-
keys = tbl.keys()
186-
keys.sort()
187-
for cat in keys:
188-
f.write(" fn %s(c: char) -> bool {\n" % cat)
189-
f.write(" ret alt c {\n")
190-
prefix = ' '
191-
for pair in tbl[cat]:
192-
if pair[0] == pair[1]:
193-
f.write(" %c %s\n" %
194-
(prefix, escape_char(pair[0])))
195-
else:
196-
f.write(" %c %s to %s\n" %
197-
(prefix,
198-
escape_char(pair[0]),
199-
escape_char(pair[1])))
200-
prefix = '|'
201-
f.write(" { true }\n")
202-
f.write(" _ { false }\n")
203-
f.write(" };\n")
204-
f.write(" }\n\n")
199+
def emit_conversions_module(f, lowerupper, upperlower):
200+
f.write("pub mod conversions {\n")
201+
f.write("""
202+
use cmp::{Equal, Less, Greater};
203+
use vec::ImmutableVector;
204+
use tuple::Tuple2;
205+
use option::{Option, Some, None};
206+
207+
pub fn to_lower(c: char) -> char {
208+
match bsearch_case_table(c, LuLl_table) {
209+
None => c,
210+
Some(index) => LuLl_table[index].val1()
211+
}
212+
}
213+
214+
pub fn to_upper(c: char) -> char {
215+
match bsearch_case_table(c, LlLu_table) {
216+
None => c,
217+
Some(index) => LlLu_table[index].val1()
218+
}
219+
}
220+
221+
fn bsearch_case_table(c: char, table: &'static [(char, char)]) -> Option<uint> {
222+
table.bsearch(|&(key, _)| {
223+
if c == key { Equal }
224+
else if key < c { Less }
225+
else { Greater }
226+
})
227+
}
228+
""");
229+
emit_caseconversion_table(f, "LuLl", upperlower)
230+
emit_caseconversion_table(f, "LlLu", lowerupper)
205231
f.write("}\n")
206232

233+
def emit_caseconversion_table(f, name, table):
234+
f.write(" static %s_table : &'static [(char, char)] = &[\n" % name)
235+
sorted_table = sorted(table.iteritems(), key=operator.itemgetter(0))
236+
ix = 0
237+
for key, value in sorted_table:
238+
f.write(ch_prefix(ix))
239+
f.write("(%s, %s)" % (escape_char(key), escape_char(value)))
240+
ix += 1
241+
f.write("\n ];\n\n")
242+
207243
def format_table_content(f, content, indent):
208244
line = " "*indent
209245
first = True
@@ -359,7 +395,8 @@ def emit_decomp_module(f, canon, compat, combine):
359395
os.remove(i);
360396
rf = open(r, "w")
361397

362-
(canon_decomp, compat_decomp, gencats, combines) = load_unicode_data("UnicodeData.txt")
398+
(canon_decomp, compat_decomp, gencats,
399+
combines, lowerupper, upperlower) = load_unicode_data("UnicodeData.txt")
363400

364401
# Preamble
365402
rf.write('''// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
@@ -379,13 +416,16 @@ def emit_decomp_module(f, canon, compat, combine):
379416
380417
''')
381418

419+
emit_bsearch_range_table(rf);
382420
emit_property_module(rf, "general_category", gencats)
383421

384422
emit_decomp_module(rf, canon_decomp, compat_decomp, combines)
385423

386424
derived = load_properties("DerivedCoreProperties.txt",
387425
["XID_Start", "XID_Continue", "Alphabetic", "Lowercase", "Uppercase"])
426+
388427
emit_property_module(rf, "derived_property", derived)
389428

390429
props = load_properties("PropList.txt", ["White_Space"])
391430
emit_property_module(rf, "property", props)
431+
emit_conversions_module(rf, lowerupper, upperlower)

src/libstd/char.rs

+72-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ use cast::transmute;
2828
use option::{None, Option, Some};
2929
use iter::{Iterator, range_step};
3030
use str::StrSlice;
31-
use unicode::{derived_property, property, general_category, decompose};
31+
use unicode::{derived_property, property, general_category, decompose, conversions};
3232

3333
#[cfg(test)] use str::OwnedStr;
3434

@@ -225,6 +225,38 @@ pub fn to_digit(c: char, radix: uint) -> Option<uint> {
225225
else { None }
226226
}
227227

228+
/// Convert a char to its uppercase equivalent
229+
///
230+
/// The case-folding performed is the common or simple mapping:
231+
/// it maps one unicode codepoint (one char in Rust) to its uppercase equivalent according
232+
/// to the Unicode database at ftp://ftp.unicode.org/Public/UNIDATA/UnicodeData.txt
233+
/// The additional SpecialCasing.txt is not considered here, as it expands to multiple
234+
/// codepoints in some cases.
235+
///
236+
/// A full reference can be found here
237+
/// http://www.unicode.org/versions/Unicode4.0.0/ch03.pdf#G33992
238+
///
239+
/// # Return value
240+
///
241+
/// Returns the char itself if no conversion was made
242+
#[inline]
243+
pub fn to_uppercase(c: char) -> char {
244+
conversions::to_upper(c)
245+
}
246+
247+
/// Convert a char to its lowercase equivalent
248+
///
249+
/// The case-folding performed is the common or simple mapping
250+
/// see `to_uppercase` for references and more information
251+
///
252+
/// # Return value
253+
///
254+
/// Returns the char itself if no conversion if possible
255+
#[inline]
256+
pub fn to_lowercase(c: char) -> char {
257+
conversions::to_lower(c)
258+
}
259+
228260
///
229261
/// Converts a number to the character representing it
230262
///
@@ -385,6 +417,8 @@ pub trait Char {
385417
fn is_digit(&self) -> bool;
386418
fn is_digit_radix(&self, radix: uint) -> bool;
387419
fn to_digit(&self, radix: uint) -> Option<uint>;
420+
fn to_lowercase(&self) -> char;
421+
fn to_uppercase(&self) -> char;
388422
fn from_digit(num: uint, radix: uint) -> Option<char>;
389423
fn escape_unicode(&self, f: |char|);
390424
fn escape_default(&self, f: |char|);
@@ -421,6 +455,10 @@ impl Char for char {
421455

422456
fn to_digit(&self, radix: uint) -> Option<uint> { to_digit(*self, radix) }
423457

458+
fn to_lowercase(&self) -> char { to_lowercase(*self) }
459+
460+
fn to_uppercase(&self) -> char { to_uppercase(*self) }
461+
424462
fn from_digit(num: uint, radix: uint) -> Option<char> { from_digit(num, radix) }
425463

426464
fn escape_unicode(&self, f: |char|) { escape_unicode(*self, f) }
@@ -516,6 +554,39 @@ fn test_to_digit() {
516554
assert_eq!('$'.to_digit(36u), None);
517555
}
518556

557+
#[test]
558+
fn test_to_lowercase() {
559+
assert_eq!('A'.to_lowercase(), 'a');
560+
assert_eq!('Ö'.to_lowercase(), 'ö');
561+
assert_eq!('ß'.to_lowercase(), 'ß');
562+
assert_eq!('Ü'.to_lowercase(), 'ü');
563+
assert_eq!('💩'.to_lowercase(), '💩');
564+
assert_eq!('Σ'.to_lowercase(), 'σ');
565+
assert_eq!('Τ'.to_lowercase(), 'τ');
566+
assert_eq!('Ι'.to_lowercase(), 'ι');
567+
assert_eq!('Γ'.to_lowercase(), 'γ');
568+
assert_eq!('Μ'.to_lowercase(), 'μ');
569+
assert_eq!('Α'.to_lowercase(), 'α');
570+
assert_eq!('Σ'.to_lowercase(), 'σ');
571+
}
572+
573+
#[test]
574+
fn test_to_uppercase() {
575+
assert_eq!('a'.to_uppercase(), 'A');
576+
assert_eq!('ö'.to_uppercase(), 'Ö');
577+
assert_eq!('ß'.to_uppercase(), 'ß'); // not ẞ: Latin capital letter sharp s
578+
assert_eq!('ü'.to_uppercase(), 'Ü');
579+
assert_eq!('💩'.to_uppercase(), '💩');
580+
581+
assert_eq!('σ'.to_uppercase(), 'Σ');
582+
assert_eq!('τ'.to_uppercase(), 'Τ');
583+
assert_eq!('ι'.to_uppercase(), 'Ι');
584+
assert_eq!('γ'.to_uppercase(), 'Γ');
585+
assert_eq!('μ'.to_uppercase(), 'Μ');
586+
assert_eq!('α'.to_uppercase(), 'Α');
587+
assert_eq!('ς'.to_uppercase(), 'Σ');
588+
}
589+
519590
#[test]
520591
fn test_is_control() {
521592
assert!('\u0000'.is_control());

0 commit comments

Comments
 (0)