elf_lojban/src/lojbanic.rs

242 lines
7.5 KiB
Rust

use crate::strange::StrRange;
const CONSONANT_LOWER: &str = "bcdfgjklmnprstvzx";
const CONSONANT_UPPER: &str = "BCDFGJKLMNPRSTVZX";
const VOICED: &str = "BDGJVZbdgjvz";
const UNVOICED: &str = "CFKPSTXcfkpstx";
const SYLLABIC_CONSONANTS: &str = "lmnrLMNR";
#[rustfmt::skip]
const VOWEL_LOWER: &str = "aeiouyáạàảãăắặằẳẵâấậầẩẫeéẹèẻẽêếệềểễiíịìỉĩoóọòỏõôốộồổỗơớợờởỡuúụùủũưứựừửữyýỵỳỷỹ";
#[rustfmt::skip]
const VOWEL_UPPER: &str = "AEIOUYÁẠÀẢÃĂẮẶẰẲẴÂẤẬẦẨẪEÉẸÈẺẼÊẾỆỀỂỄIÍỊÌỈĨOÓỌÒỎÕÔỐỘỒỔỖƠỚỢỜỞỠUÚỤÙỦŨƯỨỰỪỬỮYÝỴỲỶỸ";
const APOSTROPHE_LOWER: &str = "'";
const APOSTROPHE_UPPER: &str = "h";
const STOP: &str = ".";
const GLIDE: &str = ",";
const NUMERAL: &str = "0123456789";
const A: &str = "aáạàảãăắặằẳẵâấậầẩẫAÁẠÀẢÃĂẮẶẰẲẴÂẤẬẦẨẪ";
const E: &str = "eeéẹèẻẽêếệềểễEEÉẸÈẺẼÊẾỆỀỂỄ";
const I: &str = "iiíịìỉĩIIÍỊÌỈĨ";
const O: &str = "ooóọòỏõôốộồổỗơớợờởỡOOÓỌÒỎÕÔỐỘỒỔỖƠỚỢỜỞỠ";
const U: &str = "uuúụùủũưứựừửữUUÚỤÙỦŨƯỨỰỪỬỮ";
const Y: &str = "yyýỵỳỷỹYYÝỴỲỶỸ";
const PERMISSIBLE_INITIAL_PAIRS: &[&str] = &[
"bl", "br", "cf", "ck", "cl", "cm", "cn", "cp", "cr", "ct", "dj", "dr", "dz", "fl", "fr", "gl",
"gr", "jb", "jd", "jg", "jm", "jv", "kl", "kr", "ml", "mr", "pl", "pr", "sf", "sk", "sl", "sm",
"sn", "sp", "sr", "st", "tc", "tr", "ts", "vl", "vr", "xl", "xr", "zb", "zd", "zg", "zm", "zv",
];
pub trait Lojbanic {
fn is_lojbanic(&self) -> bool;
fn is_lojban_consonant(&self) -> bool;
fn is_lojban_voiced(&self) -> bool;
fn is_lojban_unvoiced(&self) -> bool;
fn is_lojban_syllabic_consonant(&self) -> bool;
fn is_lojban_vowel(&self) -> bool;
fn is_lojban_uppercase(&self) -> bool;
fn is_lojban_lowercase(&self) -> bool;
fn is_lojban_apostrophe(&self) -> bool;
fn is_lojban_stop(&self) -> bool;
fn is_lojban_glide(&self) -> bool;
fn is_lojban_a(&self) -> bool;
fn is_lojban_e(&self) -> bool;
fn is_lojban_i(&self) -> bool;
fn is_lojban_o(&self) -> bool;
fn is_lojban_u(&self) -> bool;
fn is_lojban_y(&self) -> bool;
}
impl Lojbanic for char {
fn is_lojbanic(&self) -> bool {
[
CONSONANT_LOWER,
CONSONANT_UPPER,
VOWEL_LOWER,
VOWEL_UPPER,
APOSTROPHE_LOWER,
APOSTROPHE_UPPER,
NUMERAL,
GLIDE,
STOP,
]
.iter()
.any(|s| s.contains(*self))
}
fn is_lojban_consonant(&self) -> bool {
self.is_lojbanic()
&& [CONSONANT_LOWER, CONSONANT_UPPER]
.iter()
.any(|s| s.contains(*self))
}
fn is_lojban_vowel(&self) -> bool {
self.is_lojbanic() && [VOWEL_LOWER, VOWEL_UPPER].iter().any(|s| s.contains(*self))
}
fn is_lojban_uppercase(&self) -> bool {
self.is_lojbanic()
&& [CONSONANT_UPPER, VOWEL_UPPER, APOSTROPHE_UPPER]
.iter()
.any(|s| s.contains(*self))
}
fn is_lojban_voiced(&self) -> bool {
self.is_lojbanic() && VOICED.contains(*self)
}
fn is_lojban_unvoiced(&self) -> bool {
self.is_lojbanic() && UNVOICED.contains(*self)
}
fn is_lojban_syllabic_consonant(&self) -> bool {
self.is_lojbanic() && SYLLABIC_CONSONANTS.contains(*self)
}
fn is_lojban_lowercase(&self) -> bool {
self.is_lojbanic() && (CONSONANT_UPPER.contains(*self) || VOWEL_LOWER.contains(*self))
}
fn is_lojban_apostrophe(&self) -> bool {
[APOSTROPHE_LOWER, APOSTROPHE_UPPER]
.iter()
.any(|s| s.contains(*self))
}
fn is_lojban_stop(&self) -> bool {
STOP.contains(*self)
}
fn is_lojban_glide(&self) -> bool {
GLIDE.contains(*self)
}
fn is_lojban_a(&self) -> bool {
A.contains(*self)
}
fn is_lojban_e(&self) -> bool {
E.contains(*self)
}
fn is_lojban_i(&self) -> bool {
I.contains(*self)
}
fn is_lojban_o(&self) -> bool {
O.contains(*self)
}
fn is_lojban_u(&self) -> bool {
U.contains(*self)
}
fn is_lojban_y(&self) -> bool {
Y.contains(*self)
}
}
macro_rules! option_passthrough_methods {
($($nym:ident),*$(,)?) => {
$(fn $nym(&self) -> bool {
self.iter().any(|ch| ch.$nym())
})*
};
}
impl<T: Lojbanic + Copy> Lojbanic for Option<T> {
// If `None`, false, otherwise `Some(ch).unwrap().{method}()`
option_passthrough_methods! {
is_lojbanic,
is_lojban_consonant,
is_lojban_vowel,
is_lojban_uppercase,
is_lojban_voiced,
is_lojban_unvoiced,
is_lojban_syllabic_consonant,
is_lojban_lowercase,
is_lojban_apostrophe,
is_lojban_stop,
is_lojban_glide,
is_lojban_a,
is_lojban_e,
is_lojban_i,
is_lojban_o,
is_lojban_u,
is_lojban_y,
}
}
pub fn is_valid_consonant_pair(left: char, right: char) -> bool {
match left {
e if e == right => false,
e if e.is_lojban_voiced() => right.is_lojban_voiced() || right.is_lojban_syllabic_consonant(),
e if e.is_lojban_unvoiced() => {
right.is_lojban_unvoiced() || right.is_lojban_syllabic_consonant()
}
e if e.is_lojban_syllabic_consonant() => true,
_ => false,
}
}
#[test]
fn consonant_pairs() {
let permissible_pairs = [
('b', "dgjvzmnlr"),
('c', "fkptlrmn"),
('d', "bgjvlmnzr"),
('f', "ckpstxmnlr"),
('g', "bdjvzmnlr"),
('j', "bdgvmlnr"),
('k', "cfpstmnlr"),
('l', "bcdfgjkpstvxzmnr"),
('m', "bcdfgjkpstvxlrn"),
('n', "bcdfgjkpstvxzlmr"),
('p', "cfkstxmnlr"),
('r', "bcdfgjkpstvxzlmn"),
('s', "fklprtmnx"),
('t', "crsfklpxmn"),
('v', "bdgjzmnlr"),
('x', "fpstmnlr"),
('z', "bdgvmlnr"),
];
for (left, s) in permissible_pairs {
for right in s.chars() {
assert![is_valid_consonant_pair(left, right)];
}
}
}
pub fn is_valid_vowel_pair(left: char, right: char) -> bool {
(match left {
e if e.is_lojban_a() => !right.is_lojban_a() && (right.is_lojban_u() || right.is_lojban_i()),
e if e.is_lojban_e() => !right.is_lojban_e() && right.is_lojban_i(),
e if e.is_lojban_i() => true,
e if e.is_lojban_o() => false,
e if e.is_lojban_u() => true,
e if e.is_lojban_y() => false,
_ => {
println!["{left}, {right}"];
unreachable![]
}
}) && !right.is_lojban_y()
}
pub fn is_valid_vowel_pair_name(left: char, right: char) -> bool {
is_valid_vowel_pair(left, right) || left.is_lojban_u() || left.is_lojban_i()
}
pub fn starts_with_permissible_initial_pair(s: &StrRange) -> bool {
if s.len() > 1 {
for pair in PERMISSIBLE_INITIAL_PAIRS {
if s.as_str().starts_with(pair) {
return true;
}
}
false
} else {
true
}
}
#[test]
fn vowel_pairs() {
let correct = [
[false, false, true, false, true, false],
[false, false, true, false, false, false],
[true, true, true, true, true, true],
[false, false, false, false, false, false],
[true, true, true, true, true, true],
[false, false, false, false, false, false],
];
for (i, left) in "aeiouy".chars().enumerate() {
for (j, right) in "aeiouy".chars().enumerate() {
assert![is_valid_vowel_pair_name(left, right) == correct[i][j]];
}
}
}