2011-12-23 20:48:08 -06:00
|
|
|
#!/usr/bin/env python
|
2014-02-02 04:47:02 -06:00
|
|
|
#
|
|
|
|
# Copyright 2011-2013 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.
|
2011-12-23 20:48:08 -06:00
|
|
|
|
|
|
|
# This digests UnicodeData.txt and DerivedCoreProperties.txt and emits rust
|
|
|
|
# code covering the core properties. Since this is a pretty rare event we
|
|
|
|
# just store this out-of-line and check the unicode.rs file into git.
|
|
|
|
#
|
2013-11-23 11:56:56 -06:00
|
|
|
# The emitted code is "the minimum we think is necessary for libstd", that
|
2011-12-23 20:48:08 -06:00
|
|
|
# is, to support basic operations of the compiler and "most nontrivial rust
|
|
|
|
# programs". It is not meant to be a complete implementation of unicode.
|
|
|
|
# For that we recommend you use a proper binding to libicu.
|
|
|
|
|
2014-02-26 06:49:56 -06:00
|
|
|
import fileinput, re, os, sys, operator
|
2011-12-23 20:48:08 -06:00
|
|
|
|
|
|
|
|
|
|
|
def fetch(f):
|
|
|
|
if not os.path.exists(f):
|
|
|
|
os.system("curl -O http://www.unicode.org/Public/UNIDATA/%s"
|
|
|
|
% f)
|
|
|
|
|
|
|
|
if not os.path.exists(f):
|
|
|
|
sys.stderr.write("cannot load %s" % f)
|
|
|
|
exit(1)
|
|
|
|
|
|
|
|
|
2011-12-29 19:24:04 -06:00
|
|
|
def load_unicode_data(f):
|
2011-12-23 20:48:08 -06:00
|
|
|
fetch(f)
|
|
|
|
gencats = {}
|
2014-02-26 06:49:56 -06:00
|
|
|
upperlower = {}
|
|
|
|
lowerupper = {}
|
2013-08-10 18:57:59 -05:00
|
|
|
combines = []
|
2011-12-29 19:24:04 -06:00
|
|
|
canon_decomp = {}
|
|
|
|
compat_decomp = {}
|
2011-12-23 20:48:08 -06:00
|
|
|
curr_cat = ""
|
2013-08-10 18:57:59 -05:00
|
|
|
curr_combine = ""
|
2011-12-23 20:48:08 -06:00
|
|
|
c_lo = 0
|
|
|
|
c_hi = 0
|
2013-08-10 18:57:59 -05:00
|
|
|
com_lo = 0
|
|
|
|
com_hi = 0
|
2014-02-26 06:49:56 -06:00
|
|
|
|
2011-12-23 20:48:08 -06:00
|
|
|
for line in fileinput.input(f):
|
|
|
|
fields = line.split(";")
|
|
|
|
if len(fields) != 15:
|
|
|
|
continue
|
|
|
|
[code, name, gencat, combine, bidi,
|
|
|
|
decomp, deci, digit, num, mirror,
|
2013-11-25 23:15:45 -06:00
|
|
|
old, iso, upcase, lowcase, titlecase ] = fields
|
2011-12-23 20:48:08 -06:00
|
|
|
|
2014-02-26 06:49:56 -06:00
|
|
|
code_org = code
|
|
|
|
code = int(code, 16)
|
|
|
|
|
|
|
|
# generate char to char direct common and simple conversions
|
|
|
|
# uppercase to lowercase
|
|
|
|
if gencat == "Lu" and lowcase != "" and code_org != lowcase:
|
|
|
|
upperlower[code] = int(lowcase, 16)
|
|
|
|
|
|
|
|
# lowercase to uppercase
|
|
|
|
if gencat == "Ll" and upcase != "" and code_org != upcase:
|
|
|
|
lowerupper[code] = int(upcase, 16)
|
2011-12-23 20:48:08 -06:00
|
|
|
|
2011-12-29 19:24:04 -06:00
|
|
|
if decomp != "":
|
|
|
|
if decomp.startswith('<'):
|
|
|
|
seq = []
|
|
|
|
for i in decomp.split()[1:]:
|
|
|
|
seq.append(int(i, 16))
|
|
|
|
compat_decomp[code] = seq
|
|
|
|
else:
|
|
|
|
seq = []
|
|
|
|
for i in decomp.split():
|
|
|
|
seq.append(int(i, 16))
|
|
|
|
canon_decomp[code] = seq
|
|
|
|
|
2011-12-23 20:48:08 -06:00
|
|
|
if curr_cat == "":
|
|
|
|
curr_cat = gencat
|
|
|
|
c_lo = code
|
|
|
|
c_hi = code
|
|
|
|
|
|
|
|
if curr_cat == gencat:
|
|
|
|
c_hi = code
|
|
|
|
else:
|
|
|
|
if curr_cat not in gencats:
|
|
|
|
gencats[curr_cat] = []
|
|
|
|
|
|
|
|
gencats[curr_cat].append((c_lo, c_hi))
|
|
|
|
curr_cat = gencat
|
|
|
|
c_lo = code
|
|
|
|
c_hi = code
|
2011-12-29 19:24:04 -06:00
|
|
|
|
2013-08-10 18:57:59 -05:00
|
|
|
if curr_combine == "":
|
|
|
|
curr_combine = combine
|
|
|
|
com_lo = code
|
|
|
|
com_hi = code
|
|
|
|
|
|
|
|
if curr_combine == combine:
|
|
|
|
com_hi = code
|
|
|
|
else:
|
|
|
|
if curr_combine != "0":
|
|
|
|
combines.append((com_lo, com_hi, curr_combine))
|
|
|
|
curr_combine = combine
|
|
|
|
com_lo = code
|
|
|
|
com_hi = code
|
|
|
|
|
2014-02-26 06:49:56 -06:00
|
|
|
return (canon_decomp, compat_decomp, gencats, combines, lowerupper, upperlower)
|
2011-12-23 20:48:08 -06:00
|
|
|
|
2013-11-25 23:15:45 -06:00
|
|
|
def load_properties(f, interestingprops):
|
2011-12-23 20:48:08 -06:00
|
|
|
fetch(f)
|
2013-11-25 23:15:45 -06:00
|
|
|
props = {}
|
2011-12-23 20:48:08 -06:00
|
|
|
re1 = re.compile("^([0-9A-F]+) +; (\w+)")
|
|
|
|
re2 = re.compile("^([0-9A-F]+)\.\.([0-9A-F]+) +; (\w+)")
|
|
|
|
|
|
|
|
for line in fileinput.input(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 prop not in interestingprops:
|
|
|
|
continue
|
|
|
|
d_lo = int(d_lo, 16)
|
|
|
|
d_hi = int(d_hi, 16)
|
2013-11-25 23:15:45 -06:00
|
|
|
if prop not in props:
|
|
|
|
props[prop] = []
|
|
|
|
props[prop].append((d_lo, d_hi))
|
|
|
|
return props
|
2011-12-23 20:48:08 -06:00
|
|
|
|
|
|
|
def escape_char(c):
|
|
|
|
if c <= 0xff:
|
|
|
|
return "'\\x%2.2x'" % c
|
|
|
|
if c <= 0xffff:
|
|
|
|
return "'\\u%4.4x'" % c
|
|
|
|
return "'\\U%8.8x'" % c
|
|
|
|
|
2013-01-08 10:44:31 -06:00
|
|
|
def ch_prefix(ix):
|
|
|
|
if ix == 0:
|
|
|
|
return " "
|
|
|
|
if ix % 2 == 0:
|
|
|
|
return ",\n "
|
|
|
|
else:
|
|
|
|
return ", "
|
|
|
|
|
|
|
|
def emit_bsearch_range_table(f):
|
|
|
|
f.write("""
|
2013-06-28 20:19:14 -05:00
|
|
|
fn bsearch_range_table(c: char, r: &'static [(char,char)]) -> bool {
|
|
|
|
use cmp::{Equal, Less, Greater};
|
2013-06-28 22:35:25 -05:00
|
|
|
use vec::ImmutableVector;
|
2013-01-08 10:44:31 -06:00
|
|
|
use option::None;
|
2013-11-23 11:56:56 -06:00
|
|
|
r.bsearch(|&(lo,hi)| {
|
2013-06-28 20:19:14 -05:00
|
|
|
if lo <= c && c <= hi { Equal }
|
|
|
|
else if hi < c { Less }
|
|
|
|
else { Greater }
|
2013-01-08 10:44:31 -06:00
|
|
|
}) != None
|
|
|
|
}\n\n
|
|
|
|
""");
|
|
|
|
|
2011-12-29 19:24:04 -06:00
|
|
|
def emit_property_module(f, mod, tbl):
|
2013-01-08 10:44:31 -06:00
|
|
|
f.write("pub mod %s {\n" % mod)
|
|
|
|
keys = tbl.keys()
|
|
|
|
keys.sort()
|
|
|
|
emit_bsearch_range_table(f);
|
2014-02-26 06:49:56 -06:00
|
|
|
|
2013-01-08 10:44:31 -06:00
|
|
|
for cat in keys:
|
2014-02-26 02:36:11 -06:00
|
|
|
if cat not in ["Nd", "Nl", "No", "Cc",
|
2014-02-26 06:49:56 -06:00
|
|
|
"XID_Start", "XID_Continue", "Alphabetic",
|
|
|
|
"Lowercase", "Uppercase", "White_Space"]:
|
|
|
|
continue
|
2013-06-28 20:19:14 -05:00
|
|
|
f.write(" static %s_table : &'static [(char,char)] = &[\n" % cat)
|
2013-01-08 10:44:31 -06:00
|
|
|
ix = 0
|
|
|
|
for pair in tbl[cat]:
|
|
|
|
f.write(ch_prefix(ix))
|
|
|
|
f.write("(%s, %s)" % (escape_char(pair[0]), escape_char(pair[1])))
|
|
|
|
ix += 1
|
|
|
|
f.write("\n ];\n\n")
|
|
|
|
|
2013-06-28 20:19:14 -05:00
|
|
|
f.write(" pub fn %s(c: char) -> bool {\n" % cat)
|
2013-01-08 10:44:31 -06:00
|
|
|
f.write(" bsearch_range_table(c, %s_table)\n" % cat)
|
|
|
|
f.write(" }\n\n")
|
|
|
|
f.write("}\n")
|
|
|
|
|
|
|
|
|
2014-02-26 06:49:56 -06:00
|
|
|
def emit_conversions_module(f, lowerupper, upperlower):
|
|
|
|
f.write("pub mod conversions {\n")
|
|
|
|
f.write("""
|
|
|
|
use cmp::{Equal, Less, Greater};
|
|
|
|
use vec::ImmutableVector;
|
|
|
|
use tuple::Tuple2;
|
|
|
|
use option::{ Option, Some, None };
|
|
|
|
|
|
|
|
pub fn to_lower(c: char) -> char {
|
|
|
|
match bsearch_case_table(c, LuLl_table) {
|
|
|
|
None => c,
|
|
|
|
Some(index) => LuLl_table[index].val1()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn to_upper(c: char) -> char {
|
|
|
|
match bsearch_case_table(c, LlLu_table) {
|
|
|
|
None => c,
|
|
|
|
Some(index) => LlLu_table[index].val1()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn bsearch_case_table(c: char, table: &'static [(char, char)]) -> Option<uint> {
|
|
|
|
table.bsearch(|&(key, _)| {
|
|
|
|
if c == key { Equal }
|
|
|
|
else if key < c { Less }
|
|
|
|
else { Greater }
|
|
|
|
})
|
|
|
|
}
|
|
|
|
""");
|
|
|
|
emit_caseconversions(f, lowerupper, upperlower)
|
2011-12-23 20:48:08 -06:00
|
|
|
f.write("}\n")
|
|
|
|
|
2014-02-26 06:49:56 -06:00
|
|
|
def emit_caseconversions(f, lowerupper, upperlower):
|
|
|
|
f.write(" static LuLl_table : &'static [(char, char)] = &[\n")
|
|
|
|
sorted_by_lu = sorted(upperlower.iteritems(), key=operator.itemgetter(0))
|
|
|
|
ix = 0
|
|
|
|
for key, value in sorted_by_lu:
|
|
|
|
f.write(ch_prefix(ix))
|
|
|
|
f.write("(%s, %s)" % (escape_char(key), escape_char(value)))
|
|
|
|
ix += 1
|
|
|
|
f.write("\n ];\n\n")
|
|
|
|
|
|
|
|
f.write(" static LlLu_table : &'static [(char, char)] = &[\n")
|
|
|
|
sorted_by_ll = sorted(lowerupper.iteritems(), key=operator.itemgetter(0))
|
|
|
|
ix = 0
|
|
|
|
for key, value in sorted_by_ll:
|
|
|
|
f.write(ch_prefix(ix))
|
|
|
|
f.write("(%s, %s)" % (escape_char(key), escape_char(value)))
|
|
|
|
ix += 1
|
|
|
|
f.write("\n ];\n\n")
|
|
|
|
|
2013-08-07 13:48:10 -05:00
|
|
|
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)
|
|
|
|
|
2013-08-10 18:57:59 -05:00
|
|
|
def emit_decomp_module(f, canon, compat, combine):
|
2011-12-29 19:24:04 -06:00
|
|
|
canon_keys = canon.keys()
|
|
|
|
canon_keys.sort()
|
|
|
|
|
|
|
|
compat_keys = compat.keys()
|
|
|
|
compat_keys.sort()
|
2013-08-07 13:48:10 -05:00
|
|
|
f.write("pub mod decompose {\n");
|
|
|
|
f.write(" use option::Option;\n");
|
|
|
|
f.write(" use option::{Some, None};\n");
|
|
|
|
f.write(" use vec::ImmutableVector;\n");
|
|
|
|
f.write("""
|
|
|
|
fn bsearch_table(c: char, r: &'static [(char, &'static [char])]) -> Option<&'static [char]> {
|
|
|
|
use cmp::{Equal, Less, Greater};
|
|
|
|
match r.bsearch(|&(val, _)| {
|
|
|
|
if c == val { Equal }
|
|
|
|
else if val < c { Less }
|
|
|
|
else { Greater }
|
|
|
|
}) {
|
|
|
|
Some(idx) => {
|
|
|
|
let (_, result) = r[idx];
|
|
|
|
Some(result)
|
|
|
|
}
|
|
|
|
None => None
|
|
|
|
}
|
2013-08-10 18:57:59 -05:00
|
|
|
}\n
|
|
|
|
""")
|
|
|
|
|
|
|
|
f.write("""
|
|
|
|
fn bsearch_range_value_table(c: char, r: &'static [(char, char, u8)]) -> u8 {
|
|
|
|
use cmp::{Equal, Less, Greater};
|
|
|
|
match r.bsearch(|&(lo, hi, _)| {
|
|
|
|
if lo <= c && c <= hi { Equal }
|
|
|
|
else if hi < c { Less }
|
|
|
|
else { Greater }
|
|
|
|
}) {
|
|
|
|
Some(idx) => {
|
|
|
|
let (_, _, result) = r[idx];
|
|
|
|
result
|
|
|
|
}
|
|
|
|
None => 0
|
|
|
|
}
|
2013-08-07 13:48:10 -05:00
|
|
|
}\n\n
|
|
|
|
""")
|
2013-08-10 18:57:59 -05:00
|
|
|
|
2013-08-07 13:48:10 -05:00
|
|
|
f.write(" // Canonical decompositions\n")
|
|
|
|
f.write(" static canonical_table : &'static [(char, &'static [char])] = &[\n")
|
|
|
|
data = ""
|
|
|
|
first = True
|
|
|
|
for char in canon_keys:
|
|
|
|
if not first:
|
|
|
|
data += ","
|
|
|
|
first = False
|
|
|
|
data += "(%s,&[" % escape_char(char)
|
|
|
|
first2 = True
|
|
|
|
for d in canon[char]:
|
|
|
|
if not first2:
|
|
|
|
data += ","
|
|
|
|
first2 = False
|
|
|
|
data += escape_char(d)
|
|
|
|
data += "])"
|
|
|
|
format_table_content(f, data, 8)
|
|
|
|
f.write("\n ];\n\n")
|
2013-08-10 18:57:59 -05:00
|
|
|
|
2013-08-07 13:48:10 -05:00
|
|
|
f.write(" // Compatibility decompositions\n")
|
|
|
|
f.write(" static compatibility_table : &'static [(char, &'static [char])] = &[\n")
|
|
|
|
data = ""
|
|
|
|
first = True
|
|
|
|
for char in compat_keys:
|
|
|
|
if not first:
|
|
|
|
data += ","
|
|
|
|
first = False
|
|
|
|
data += "(%s,&[" % escape_char(char)
|
|
|
|
first2 = True
|
|
|
|
for d in compat[char]:
|
|
|
|
if not first2:
|
|
|
|
data += ","
|
|
|
|
first2 = False
|
|
|
|
data += escape_char(d)
|
|
|
|
data += "])"
|
|
|
|
format_table_content(f, data, 8)
|
|
|
|
f.write("\n ];\n\n")
|
2013-08-10 18:57:59 -05:00
|
|
|
|
|
|
|
f.write(" static combining_class_table : &'static [(char, char, u8)] = &[\n")
|
|
|
|
ix = 0
|
|
|
|
for pair in combine:
|
|
|
|
f.write(ch_prefix(ix))
|
|
|
|
f.write("(%s, %s, %s)" % (escape_char(pair[0]), escape_char(pair[1]), pair[2]))
|
|
|
|
ix += 1
|
|
|
|
f.write("\n ];\n")
|
|
|
|
|
2013-11-23 11:56:56 -06:00
|
|
|
f.write(" pub fn canonical(c: char, i: |char|) "
|
2013-08-07 13:48:10 -05:00
|
|
|
+ "{ d(c, i, false); }\n\n")
|
2013-11-23 11:56:56 -06:00
|
|
|
f.write(" pub fn compatibility(c: char, i: |char|) "
|
2011-12-29 19:30:43 -06:00
|
|
|
+"{ d(c, i, true); }\n\n")
|
2013-08-10 18:57:59 -05:00
|
|
|
f.write(" pub fn canonical_combining_class(c: char) -> u8 {\n"
|
|
|
|
+ " bsearch_range_value_table(c, combining_class_table)\n"
|
|
|
|
+ " }\n\n")
|
2013-11-23 11:56:56 -06:00
|
|
|
f.write(" fn d(c: char, i: |char|, k: bool) {\n")
|
2013-09-08 10:01:16 -05:00
|
|
|
f.write(" use iter::Iterator;\n");
|
2011-12-29 19:24:04 -06:00
|
|
|
|
2013-08-07 13:48:10 -05:00
|
|
|
f.write(" if c <= '\\x7f' { i(c); return; }\n")
|
2011-12-29 19:24:04 -06:00
|
|
|
|
|
|
|
# First check the canonical decompositions
|
2013-08-07 13:48:10 -05:00
|
|
|
f.write("""
|
|
|
|
match bsearch_table(c, canonical_table) {
|
|
|
|
Some(canon) => {
|
|
|
|
for x in canon.iter() {
|
|
|
|
d(*x, |b| i(b), k);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
None => ()
|
|
|
|
}\n\n""")
|
2011-12-29 19:24:04 -06:00
|
|
|
|
|
|
|
# Bottom out if we're not doing compat.
|
2013-08-07 13:48:10 -05:00
|
|
|
f.write(" if !k { i(c); return; }\n")
|
2011-12-29 19:24:04 -06:00
|
|
|
|
|
|
|
# Then check the compatibility decompositions
|
2013-08-07 13:48:10 -05:00
|
|
|
f.write("""
|
|
|
|
match bsearch_table(c, compatibility_table) {
|
|
|
|
Some(compat) => {
|
|
|
|
for x in compat.iter() {
|
|
|
|
d(*x, |b| i(b), k);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
None => ()
|
|
|
|
}\n\n""")
|
2011-12-29 19:24:04 -06:00
|
|
|
|
|
|
|
# Finally bottom out.
|
|
|
|
f.write(" i(c);\n")
|
|
|
|
f.write(" }\n")
|
|
|
|
f.write("}\n\n")
|
2011-12-23 20:48:08 -06:00
|
|
|
|
|
|
|
r = "unicode.rs"
|
2011-12-29 19:24:04 -06:00
|
|
|
for i in [r]:
|
2011-12-23 20:48:08 -06:00
|
|
|
if os.path.exists(i):
|
|
|
|
os.remove(i);
|
|
|
|
rf = open(r, "w")
|
|
|
|
|
2014-02-26 06:49:56 -06:00
|
|
|
(canon_decomp, compat_decomp, gencats,
|
|
|
|
combines, lowerupper, upperlower) = load_unicode_data("UnicodeData.txt")
|
2013-05-02 05:37:57 -05:00
|
|
|
|
2013-06-28 20:19:14 -05:00
|
|
|
# Preamble
|
|
|
|
rf.write('''// Copyright 2012-2013 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.
|
|
|
|
|
|
|
|
// The following code was generated by "src/etc/unicode.py"
|
|
|
|
|
|
|
|
#[allow(missing_doc)];
|
2013-06-30 22:51:13 -05:00
|
|
|
#[allow(non_uppercase_statics)];
|
2013-06-28 20:19:14 -05:00
|
|
|
|
|
|
|
''')
|
2013-05-02 05:37:57 -05:00
|
|
|
|
2011-12-29 19:24:04 -06:00
|
|
|
emit_property_module(rf, "general_category", gencats)
|
2011-12-23 20:48:08 -06:00
|
|
|
|
2013-08-10 18:57:59 -05:00
|
|
|
emit_decomp_module(rf, canon_decomp, compat_decomp, combines)
|
2013-01-08 10:44:31 -06:00
|
|
|
|
2013-11-25 23:15:45 -06:00
|
|
|
derived = load_properties("DerivedCoreProperties.txt",
|
|
|
|
["XID_Start", "XID_Continue", "Alphabetic", "Lowercase", "Uppercase"])
|
2014-02-26 06:49:56 -06:00
|
|
|
|
2011-12-29 19:30:43 -06:00
|
|
|
emit_property_module(rf, "derived_property", derived)
|
2013-11-25 23:15:45 -06:00
|
|
|
|
|
|
|
props = load_properties("PropList.txt", ["White_Space"])
|
|
|
|
emit_property_module(rf, "property", props)
|
2014-02-26 06:49:56 -06:00
|
|
|
emit_conversions_module(rf, lowerupper, upperlower)
|