///|
/// The version of the Unicode Character Database the tables come from.
pub fn version() -> String {
ucd_version
}
///|
/// General_Category.
pub(all) enum Category {
Lu
Ll
Lt
Lm
Lo
Mn
Mc
Me
Nd
Nl
No
Pc
Pd
Ps
Pe
Pi
Pf
Po
Sm
Sc
Sk
So
Zs
Zl
Zp
Cc
Cf
Cs
Co
Cn
} derive(Eq)
///|
pub extend Category with Eq::{equal, not_equal}
///|
pub extend Category with Show::{output, to_string}
///|
let categories : FixedArray[Category] = [
Lu,
Ll,
Lt,
Lm,
Lo,
Mn,
Mc,
Me,
Nd,
Nl,
No,
Pc,
Pd,
Ps,
Pe,
Pi,
Pf,
Po,
Sm,
Sc,
Sk,
So,
Zs,
Zl,
Zp,
Cc,
Cf,
Cs,
Co,
Cn,
]
///|
let category_names : FixedArray[String] = [
"Lu", "Ll", "Lt", "Lm", "Lo", "Mn", "Mc", "Me", "Nd", "Nl", "No", "Pc", "Pd", "Ps",
"Pe", "Pi", "Pf", "Po", "Sm", "Sc", "Sk", "So", "Zs", "Zl", "Zp", "Cc", "Cf", "Cs",
"Co", "Cn",
]
///|
/// The two-letter abbreviation, as the UCD writes it.
pub impl Show for Category with fn output(self, logger) {
for i, c in categories {
if c == self {
logger.write_string(category_names[i])
return
}
}
}
///|
/// The category of `ch`; unassigned code points are `Cn`.
pub fn category(ch : Char) -> Category {
match find_run(category_starts, category_ends, ch.to_int()) {
Some(i) => categories[category_values[i]]
None => Cn
}
}
///|
/// Bidi_Class.
pub(all) enum Bidi {
L
R
AL
EN
ES
ET
AN
CS
NSM
BN
B
S
WS
ON
LRE
LRO
RLE
RLO
PDF
LRI
RLI
FSI
PDI
} derive(Eq)
///|
pub extend Bidi with Eq::{equal, not_equal}
///|
let bidis : FixedArray[Bidi] = [
L,
R,
AL,
EN,
ES,
ET,
AN,
CS,
NSM,
BN,
B,
S,
WS,
ON,
LRE,
LRO,
RLE,
RLO,
PDF,
LRI,
RLI,
FSI,
PDI,
]
///|
/// The bidirectional class of `ch`, with the defaults the UCD assigns to
/// unassigned code points.
pub fn bidi(ch : Char) -> Bidi {
match find_run(bidi_starts, bidi_ends, ch.to_int()) {
Some(i) => bidis[bidi_values[i]]
None => L
}
}
///|
/// Numeric_Type, for characters that have one.
pub(all) enum Numeric {
Decimal
Digit
Numeric
} derive(Eq)
///|
pub extend Numeric with Eq::{equal, not_equal}
///|
/// The numeric type of `ch`; `None` for characters without a numeric value.
pub fn numeric(ch : Char) -> Numeric? {
match find_run(numeric_starts, numeric_ends, ch.to_int()) {
Some(i) =>
Some(
match numeric_values[i] {
0 => Decimal
1 => Digit
_ => Numeric
},
)
None => None
}
}
///|
/// A binary property from PropList and DerivedCoreProperties.
pub(all) enum Property {
Alphabetic
Lowercase
Uppercase
Cased
CaseIgnorable
ChangesWhenLowercased
ChangesWhenUppercased
ChangesWhenTitlecased
ChangesWhenCasefolded
ChangesWhenCasemapped
IdStart
IdContinue
XidStart
XidContinue
DefaultIgnorableCodePoint
GraphemeExtend
GraphemeBase
Math
WhiteSpace
BidiControl
JoinControl
Dash
Hyphen
QuotationMark
TerminalPunctuation
HexDigit
AsciiHexDigit
Ideographic
Diacritic
Extender
NoncharacterCodePoint
IdsBinaryOperator
IdsTrinaryOperator
Radical
UnifiedIdeograph
Deprecated
SoftDotted
LogicalOrderException
SentenceTerminal
VariationSelector
PatternWhiteSpace
PatternSyntax
PrependedConcatenationMark
RegionalIndicator
} derive(Eq)
///|
pub extend Property with Eq::{equal, not_equal}
///|
let properties : FixedArray[Property] = [
Alphabetic,
Lowercase,
Uppercase,
Cased,
CaseIgnorable,
ChangesWhenLowercased,
ChangesWhenUppercased,
ChangesWhenTitlecased,
ChangesWhenCasefolded,
ChangesWhenCasemapped,
IdStart,
IdContinue,
XidStart,
XidContinue,
DefaultIgnorableCodePoint,
GraphemeExtend,
GraphemeBase,
Math,
WhiteSpace,
BidiControl,
JoinControl,
Dash,
Hyphen,
QuotationMark,
TerminalPunctuation,
HexDigit,
AsciiHexDigit,
Ideographic,
Diacritic,
Extender,
NoncharacterCodePoint,
IdsBinaryOperator,
IdsTrinaryOperator,
Radical,
UnifiedIdeograph,
Deprecated,
SoftDotted,
LogicalOrderException,
SentenceTerminal,
VariationSelector,
PatternWhiteSpace,
PatternSyntax,
PrependedConcatenationMark,
RegionalIndicator,
]
///|
/// Whether `ch` has `property`.
pub fn has(ch : Char, property : Property) -> Bool {
let mut index = 0
while properties[index] != property {
index += 1
}
let lo = property_offsets[index] / 2
let hi = property_offsets[index + 1] / 2
let cp = ch.to_int()
let mut a = lo
let mut b = hi
while a < b {
let mid = (a + b) / 2
if property_ranges[mid * 2 + 1] < cp {
a = mid + 1
} else {
b = mid
}
}
a < hi && property_ranges[a * 2] <= cp
}
///|
/// `text` in uppercase, with the full mappings: `ß` becomes `SS`.
pub fn upper(text : StringView) -> String {
let out = StringBuilder()
for ch in text {
map_into(out, ch, upper_keys, upper_offsets, upper_values)
}
out.to_string()
}
///|
/// `text` in lowercase, with the full mappings; a capital sigma at the end
/// of a word becomes `ς`.
pub fn lower(text : StringView) -> String {
let chars = text.iter().collect()
let out = StringBuilder()
for i, ch in chars {
if ch == 'Σ' && final_sigma(chars, i) {
out.write_char('ς')
} else {
map_into(out, ch, lower_keys, lower_offsets, lower_values)
}
}
out.to_string()
}
///|
/// Every character of `text` mapped to its titlecase form. Words are not
/// found here: callers decide which characters start one.
pub fn title(text : StringView) -> String {
let out = StringBuilder()
for ch in text {
map_into(out, ch, title_keys, title_offsets, title_values)
}
out.to_string()
}
///|
/// `text` case-folded with the full foldings, for caseless comparison.
pub fn fold(text : StringView) -> String {
let out = StringBuilder()
for ch in text {
map_into(out, ch, fold_keys, fold_offsets, fold_values)
}
out.to_string()
}
///|
/// The Final_Sigma condition: a cased letter before, skipping case-ignorable
/// ones, and none after.
fn final_sigma(chars : Array[Char], at : Int32) -> Bool {
let mut i = at - 1
while i >= 0 && has(chars[i], CaseIgnorable) {
i -= 1
}
if i < 0 || !has(chars[i], Cased) {
return false
}
let mut j = at + 1
while j < chars.length() && has(chars[j], CaseIgnorable) {
j += 1
}
j >= chars.length() || !has(chars[j], Cased)
}
///|
fn map_into(
out : StringBuilder,
ch : Char,
keys : FixedArray[Int32],
offsets : FixedArray[Int32],
values : FixedArray[Int32],
) -> Unit {
let cp = ch.to_int()
let mut a = 0
let mut b = keys.length()
while a < b {
let mid = (a + b) / 2
if keys[mid] < cp {
a = mid + 1
} else {
b = mid
}
}
if a < keys.length() && keys[a] == cp {
for k in offsets[a].. Int32? {
let mut a = 0
let mut b = starts.length()
while a < b {
let mid = (a + b) / 2
if ends[mid] < cp {
a = mid + 1
} else {
b = mid
}
}
if a < starts.length() && starts[a] <= cp {
Some(a)
} else {
None
}
}