///|
fn tone_vowel_index(base : String) -> Int {
let mut a_index = -1
let mut e_index = -1
let mut o_index = -1
let mut last_vowel = -1
for index = 0; index < base.length(); index = index + 1 {
let code = base[index].to_int()
if code == 97 {
a_index = index
} else if code == 101 || code == 0x00EA {
e_index = index
} else if code == 111 {
o_index = index
}
if code == 97 ||
code == 101 ||
code == 105 ||
code == 111 ||
code == 117 ||
code == 0x00FC {
last_vowel = index
}
}
if a_index >= 0 {
return a_index
}
if e_index >= 0 {
return e_index
}
if o_index >= 0 && syllable_ascii_slice(base, o_index, o_index + 2) == "ou" {
return o_index
}
if last_vowel < 0 {
if base == "m" || base == "n" || base == "ng" {
return 0
}
if syllable_ends_with(base, "ng") {
return base.length() - 2
}
if syllable_ends_with(base, "m") || syllable_ends_with(base, "n") {
return base.length() - 1
}
}
last_vowel
}
///|
fn tone_mark_code(base_code : Int, tone : Int) -> Int {
match (base_code, tone) {
(97, 1) => 0x0101
(97, 2) => 0x00E1
(97, 3) => 0x01CE
(97, 4) => 0x00E0
(101, 1) => 0x0113
(101, 2) => 0x00E9
(101, 3) => 0x011B
(101, 4) => 0x00E8
(105, 1) => 0x012B
(105, 2) => 0x00ED
(105, 3) => 0x01D0
(105, 4) => 0x00EC
(111, 1) => 0x014D
(111, 2) => 0x00F3
(111, 3) => 0x01D2
(111, 4) => 0x00F2
(117, 1) => 0x016B
(117, 2) => 0x00FA
(117, 3) => 0x01D4
(117, 4) => 0x00F9
(0x00FC, 1) => 0x01D6
(0x00FC, 2) => 0x01D8
(0x00FC, 3) => 0x01DA
(0x00FC, 4) => 0x01DC
(110, 2) => 0x0144
(110, 3) => 0x0148
(110, 4) => 0x01F9
(109, 2) => 0x1E3F
(0x00EA, 2) => 0x1EBF
(0x00EA, 4) => 0x1EC1
_ => base_code
}
}
///|
fn uppercase_code(code : Int) -> Int {
match code {
97..=122 => code - 32
0x0101 => 0x0100
0x00E1 => 0x00C1
0x01CE => 0x01CD
0x00E0 => 0x00C0
0x0113 => 0x0112
0x00E9 => 0x00C9
0x011B => 0x011A
0x00E8 => 0x00C8
0x012B => 0x012A
0x00ED => 0x00CD
0x01D0 => 0x01CF
0x00EC => 0x00CC
0x014D => 0x014C
0x00F3 => 0x00D3
0x01D2 => 0x01D1
0x00F2 => 0x00D2
0x016B => 0x016A
0x00FA => 0x00DA
0x01D4 => 0x01D3
0x00F9 => 0x00D9
0x00FC => 0x00DC
0x01D6 => 0x01D5
0x01D8 => 0x01D7
0x01DA => 0x01D9
0x01DC => 0x01DB
0x00EA => 0x00CA
0x0144 => 0x0143
0x0148 => 0x0147
0x01F9 => 0x01F8
0x1E3F => 0x1E3E
0x1EBF => 0x1EBE
0x1EC1 => 0x1EC0
_ => code
}
}
///|
fn format_uppercase(text : String) -> String {
let mut output = ""
for index = 0; index < text.length(); index = index + 1 {
output = output +
uppercase_code(text[index].to_int())
.to_uint16()
.unsafe_to_char()
.to_string()
}
output
}
///|
/// Renders a validated syllable in a requested tone style and case.
fn format_numbered(base : String, tone : Int) -> String {
if tone == 0 {
base
} else {
base + tone.to_string()
}
}
///|
fn format_marked(base : String, tone : Int) -> String {
if tone == 0 {
return base
}
let mark_index = tone_vowel_index(base)
let mut marked = ""
for index = 0; index < base.length(); index = index + 1 {
let code = base[index].to_int()
let output_text = if index == mark_index &&
(
(code == 0x00EA && (tone == 1 || tone == 3)) ||
(code == 109 && tone != 2) ||
(code == 110 && tone == 1)
) {
let combining = match tone {
1 => 0x0304
2 => 0x0301
3 => 0x030C
_ => 0x0300
}
code.to_uint16().unsafe_to_char().to_string() +
combining.to_uint16().unsafe_to_char().to_string()
} else {
let output_code = if index == mark_index {
tone_mark_code(code, tone)
} else {
code
}
output_code.to_uint16().unsafe_to_char().to_string()
}
marked = marked + output_text
}
marked
}
///|
pub fn Syllable::format(
self : Syllable,
style : ToneStyle,
text_case : TextCase,
) -> String {
let result = match style {
PlainPinyin => self.base_value
ToneNumbers => format_numbered(self.base_value, self.tone_value)
ToneMarks => format_marked(self.base_value, self.tone_value)
}
match text_case {
Lowercase => result
Uppercase => format_uppercase(result)
}
}