use crate::{characters::*, etc::find_integer, Rules, Token};
use std::borrow::Cow;
const MAX_CHUNK: usize = 3;
pub const fn consonant_char(slice: &[char]) -> Option<char> {
Some(match slice {
['t'] => TEMA_TINCO.single_dn,
['d'] => TEMA_TINCO.double_dn,
['þ']
| ['t', 'h'] => TEMA_TINCO.single_up,
['ð']
| ['d', 'h'] => TEMA_TINCO.double_up,
['n', 'n'] => TEMA_TINCO.double_sh,
['n'] => TEMA_TINCO.single_sh,
['p'] => TEMA_PARMA.single_dn,
['b'] => TEMA_PARMA.double_dn,
['f'] => TEMA_PARMA.single_up,
['v'] => TEMA_PARMA.double_up,
['m', 'm'] => TEMA_PARMA.double_sh,
['m'] => TEMA_PARMA.single_sh,
['c']
| ['k'] => TEMA_CALMA.single_dn,
['g'] => TEMA_CALMA.double_dn,
['c', 'h']
| ['k', 'h'] => TEMA_CALMA.single_up,
['g', 'h'] => TEMA_CALMA.double_up,
['w'] => TEMA_QESSE.single_sh,
['r'] => TENGWA_ROMEN,
['l'] => TENGWA_LAMBE,
['s'] => TENGWA_SILME,
['s', 's']
| ['z'] => TENGWA_ESSE,
['h'] => TENGWA_HYARMEN,
['h', 'w'] => TENGWA_HWESTA_SINDARINWA,
['j'] => TENGWA_YANTA,
_ => { return None; }
})
}
const fn get_consonant(slice: &[char]) -> Option<Glyph> {
match consonant_char(slice) {
Some(cons) => Some(Glyph::new_cons(cons, false)),
None => None,
}
}
pub const fn get_diphthong(slice: &[char]) -> Option<Glyph> {
match slice {
['o', 'e'] => Some(Glyph::new_both(TEMA_CALMA.single_sh, TEHTA_CIRCUMFLEX)),
['a', 'i'] => Some(Glyph::new_both(TENGWA_OSSE, TEHTA_Y)),
['e', 'i'] => Some(Glyph::new_both(TENGWA_YANTA, TEHTA_Y)),
['u', 'i'] => Some(Glyph::new_both(TENGWA_URE, TEHTA_Y)),
['a', 'u']
| ['a', 'w'] => Some(Glyph::new_cons(TENGWA_OSSE, false).with_labial()),
_ => None,
}
}
pub const fn get_vowel(slice: &[char]) -> Option<Glyph> {
Some(match slice {
['a'] | ['ä'] => Glyph::new_cons(TENGWA_OSSE, false),
['e'] | ['ë'] => Glyph::new_cons(TENGWA_YANTA, false),
['i'] | ['ï'] => Glyph::new_cons(CARRIER_SHORT, false),
['o'] | ['ö'] => Glyph::new_cons(TEMA_CALMA.single_sh, false),
['u'] | ['ü'] => Glyph::new_cons(TENGWA_URE, false),
['y'] | ['ÿ'] => Glyph::new_cons(TENGWA_SILME_NUQ, false),
['á'] | ['â'] | ['a', 'a'] => Glyph::new_both(TENGWA_OSSE, TEHTA_E),
['é'] | ['ê'] | ['e', 'e'] => Glyph::new_both(TENGWA_YANTA, TEHTA_E),
['í'] | ['î'] | ['i', 'i'] => Glyph::new_both(CARRIER_SHORT, TEHTA_E),
['ó'] | ['ô'] | ['o', 'o'] => Glyph::new_both(TEMA_CALMA.single_sh, TEHTA_E),
['ú'] | ['û'] | ['u', 'u'] => Glyph::new_both(TENGWA_URE, TEHTA_E),
['ý'] | ['ŷ'] | ['y', 'y'] => Glyph::new_both(TENGWA_SILME_NUQ, TEHTA_E),
_ => { return None; }
})
}
pub const fn punctuation(slice: &[char]) -> Option<&'static str> {
match slice {
['\''] => Some(PUNCT_DOT_1),
[','] => Some(PUNCT_DOT_1),
['.'] => Some(PUNCT_DOT_2),
[':'] => Some(PUNCT_DOT_3),
[' ', ',', ' ']
| [',', ' '] => Some(PUNCT_DOT_S1),
['.', '.', '.'] => Some(PUNCT_DOT_DIAM),
[' ', ';', ' ']
| [';', ' '] => Some(PUNCT_LINE_S1),
[';'] => Some(PUNCT_LINE_1),
['-'] => Some(PUNCT_LINE_2),
['?'] => Some(PUNCT_INTERR),
['!'] => Some(PUNCT_EXCLAM),
['(']
| [')'] => Some(PUNCT_PAREN),
['['] => Some(PUNCT_PAREN_L),
[']'] => Some(PUNCT_PAREN_R),
['“'] => Some(PUNCT_PAREN_L),
['”'] => Some(PUNCT_PAREN_R),
_ => None,
}
}
pub struct Beleriand;
impl Rules for Beleriand {
fn transcribe(input: impl AsRef<str>) -> String {
let cvec: Vec<char> = input.as_ref().to_lowercase().chars().collect();
let mut line: &[char] = cvec.as_slice();
let mut out: Vec<Token> = Vec::new();
let mut tengwa: Option<Glyph> = None;
let mut sub: &[char];
let mut len: usize;
macro_rules! advance {
() => { line = &line[1..]; };
($n:expr) => { line = &line[$n..]; };
}
macro_rules! commit {
() => { if let Some(mut g) = tengwa.take() {
g.long_first = true;
out.push(Token::Tengwa(g));
} };
}
macro_rules! pass {
() => { out.push(Token::Char(line[0])); };
}
'next_slice:
while !line.is_empty() {
if let Some((number, size)) = find_integer::<isize>(line) {
commit!();
out.push(Token::String(Cow::Owned(int_12(number))));
advance!(size);
continue 'next_slice;
}
len = MAX_CHUNK;
'same_slice:
while len > 0 {
len = line.len().min(len);
sub = &line[..len];
if let Some(current) = &mut tengwa {
match sub {
&['w'] => {
if let Some(cons) = current.cons {
if (cons == TEMA_TINCO.double_dn
|| cons == TEMA_CALMA.double_dn
) && !current.labial {
current.labial = true;
advance!();
continue 'next_slice;
}
}
}
_ => {}
}
len -= 1;
if len > 0 {
continue 'same_slice;
} else {
commit!();
continue 'next_slice;
}
}
else {
if sub == ['x'] {
out.push(Token::Tengwa(Glyph::new_cons(
TEMA_CALMA.single_dn,
false,
)));
tengwa = Some(Glyph::new_cons(TENGWA_SILME, false));
advance!();
continue 'next_slice;
}
if !matches!(out.last(), Some(Token::Tengwa(Glyph { .. }))) {
match sub {
['l', 'h'] => {
tengwa = Some(Glyph::new_cons(TENGWA_ALDA, false));
advance!(sub.len());
continue 'next_slice;
}
['r', 'h'] => {
tengwa = Some(Glyph::new_cons(TENGWA_ARDA, false));
advance!(sub.len());
continue 'next_slice;
}
_ => {}
}
}
if let Some(punct) = punctuation(sub) {
out.push(Token::String(Cow::Borrowed(punct)));
advance!(sub.len());
continue 'next_slice;
}
else if let Some(new) = get_consonant(sub) {
tengwa = Some(new);
advance!(sub.len());
continue 'next_slice;
}
else if let Some(new) = get_diphthong(sub) {
tengwa = Some(new);
advance!(sub.len());
continue 'next_slice;
}
else if let Some(vowel) = get_vowel(sub) {
tengwa = Some(vowel);
advance!(sub.len());
continue 'next_slice;
}
if let ['m', rest @ ..] | ['n', rest @ ..] = sub {
if let Some(new) = get_consonant(rest) {
tengwa = Some(new.with_nasal());
advance!(sub.len());
continue 'next_slice;
}
}
len -= 1;
if len > 0 {
continue 'same_slice;
} else {
if let Some(Token::Tengwa(
Glyph { cons: Some(cons), .. }
)) = out.last_mut() {
if *cons == TENGWA_ROMEN {
*cons = TEMA_TINCO.single_sh;
}
}
pass!();
advance!();
continue 'next_slice;
}
}
}
unreachable!();
}
commit!();
if let Some(Token::Tengwa(
Glyph { cons: Some(cons), .. }
)) = out.last_mut() {
if *cons == TENGWA_ROMEN {
*cons = TEMA_TINCO.single_sh;
}
}
out.iter().map(|t| t.to_string()).collect()
}
}