use std :: { cell::RefCell, collections::{ HashMap, VecDeque }, fmt };
use crate :: {
error :: RuleRuntimeError,
rule :: { Alpha, AlphaMod, ModKind, Modifiers, Position, SpecMod, SupraSegs },
word :: Segment,
};
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub enum StressKind {
#[default]
Unstressed,
Secondary,
Primary,
}
impl fmt::Display for StressKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
StressKind::Unstressed => write!(f, "-"),
StressKind::Secondary => write!(f, "S"),
StressKind::Primary => write!(f, "P"),
}
}
}
pub type Tone = u16;
#[derive(Default, Debug, Clone)]
pub struct Syllable {
pub segments: VecDeque<Segment>,
pub stress: StressKind,
pub tone: Tone
}
impl Syllable {
pub fn new() -> Self {
Self {segments: VecDeque::new(), stress: StressKind::default(), tone: 0}
}
pub fn seg_indices(&self) -> Vec<usize> {
let mut vec = Vec::new();
let mut i = 0;
while i < self.segments.len() {
let len = self.get_seg_length_at(i);
vec.push(i);
i+=len;
}
vec
}
pub(crate) fn insert_segment(&mut self, pos: usize, seg: &Segment, mods: &Option<Modifiers>, alphas: &RefCell<HashMap<char, Alpha>>, err_pos: Position) -> Result<i8, RuleRuntimeError> {
let mut lc = 0;
if pos > self.segments.len() {
self.segments.push_back(*seg);
} else {
self.segments.insert(pos, *seg);
}
if let Some(m) = mods {
lc += self.apply_seg_mods(alphas, m, pos, err_pos)?;
}
Ok(lc)
}
pub(crate) fn get_seg_length_at(&self, pos: usize) -> usize {
debug_assert!(pos < self.segments.len());
let mut s_i = pos + 1;
let mut len = 1;
while s_i < self.segments.len() && self.segments[pos] == self.segments[s_i] {
len +=1; s_i += 1;
}
len
}
pub(crate) fn apply_seg_mods(&mut self, alphas: &RefCell<HashMap<char, Alpha>>, mods: &Modifiers, start_pos: usize, err_pos: Position) -> Result<i8, RuleRuntimeError> {
let mut pos = start_pos;
let mut seg_len = self.get_seg_length_at(pos);
while seg_len > 0 {
let seg = self.segments.get_mut(pos).expect("position is in bounds");
seg.apply_seg_mods(alphas, mods.nodes, mods.feats, err_pos, false)?;
seg_len -= 1;
pos +=1;
}
self.apply_supras(alphas, &mods.suprs, start_pos, err_pos)
}
pub(crate) fn calc_new_length(alphas: &RefCell<HashMap<char, Alpha>>, mods: &SupraSegs, old_len: u8, err_pos: Position) -> Result<u8, RuleRuntimeError> {
let mut len_change: i8 = 0;
if let Some(len_mods) = mods.length {
match len_mods {
SpecMod::Second(v) => if v.as_bool(alphas, err_pos)? {
if old_len < 3 {
len_change = 3 - old_len as i8;
}
} else if old_len > 2 {
len_change = 2 - old_len as i8;
},
SpecMod::First(long) => if long.as_bool(alphas, err_pos)? {
if old_len < 2 {
len_change = 1 }
} else if old_len > 1 {
len_change = 1 - old_len as i8;
},
SpecMod::Both(long, vlong) => match (long.as_bool(alphas, err_pos)?, vlong.as_bool(alphas, err_pos)?) {
(true, true) => if old_len < 3 {
len_change = 3 - old_len as i8;
},
(true, false) => if old_len > 2 {
len_change = 2 - old_len as i8;
} else if old_len < 2 {
len_change = 1;
},
(false, false) => if old_len > 1 {
len_change = 1 - old_len as i8;
},
(false, true) => return Err(RuleRuntimeError::OverlongPosLongNeg(err_pos)),
},
SpecMod::Joined(val) => match val {
ModKind::Binary(_) => return Err(RuleRuntimeError::GroupSuprIsBinary("length", err_pos)),
ModKind::Alpha(am) => match am {
AlphaMod::InvAlpha(_) => return Err(RuleRuntimeError::GroupSuprIsInverted("length", err_pos)),
AlphaMod::Alpha(ch) => {
if let Some(alph) = alphas.borrow().get(&ch) {
let Some(group) = alph.as_grouped() else { return Err(RuleRuntimeError::AlphaIsNotSuprGroup(err_pos)) };
match group {
(true, true) => if old_len < 3 {
len_change = 3 - old_len as i8;
println!("{old_len} {len_change}");
},
(true, false) => if old_len > 2 {
len_change = 2 - old_len as i8;
} else if old_len < 2 {
len_change = 1;
},
(false, false) => if old_len > 1 {
len_change = 1 - old_len as i8;
},
(false, true) => return Err(RuleRuntimeError::OverlongPosLongNeg(err_pos)),
}
} else {
return Err(RuleRuntimeError::AlphaUnknown(err_pos))
}
},
},
},
}
}
debug_assert!(old_len.saturating_add_signed(len_change) > 0, "{old_len} < {len_change}");
Ok((old_len as i8 + len_change) as u8)
}
pub(crate) fn apply_supras(&mut self, alphas: &RefCell<HashMap<char, Alpha>>, mods: &SupraSegs, pos: usize, err_pos: Position) -> Result<i8, RuleRuntimeError> {
let seg = self.segments[pos];
let cur_len = self.get_seg_length_at(pos) as u8;
let new_len = Self::calc_new_length(alphas, mods, cur_len, err_pos)?;
if new_len > cur_len {
for _ in 1..new_len {
self.segments.insert(pos, seg);
}
} else if new_len < cur_len {
for _ in 1..new_len {
self.segments.remove(pos);
}
}
self.apply_syll_mods(alphas, mods, err_pos)?;
Ok(new_len as i8 - cur_len as i8)
}
pub(crate) fn apply_syll_mods(&mut self, alphas: &RefCell<HashMap<char, Alpha>>, mods: &SupraSegs, err_pos: Position) -> Result<(), RuleRuntimeError> {
if let Some(str_mods) = mods.stress {
match str_mods {
SpecMod::First(prim) => if prim.as_bool(alphas, err_pos)? {
self.stress = StressKind::Primary;
} else {
self.stress = StressKind::Unstressed;
},
SpecMod::Second(sec) => if sec.as_bool(alphas, err_pos)? {
self.stress = StressKind::Secondary;
} else if self.stress == StressKind::Secondary {
self.stress = StressKind::Unstressed;
},
SpecMod::Both(prim, sec) => match (prim.as_bool(alphas, err_pos)?, sec.as_bool(alphas, err_pos)?) {
(true, true) => self.stress = StressKind::Secondary,
(true, false) => self.stress = StressKind::Primary,
(false, false) => self.stress = StressKind::Unstressed,
(false, true) => return Err(RuleRuntimeError::SecStrPosStrNeg(err_pos)),
},
SpecMod::Joined(val) => match val {
ModKind::Binary(_) => return Err(RuleRuntimeError::GroupSuprIsBinary("stress", err_pos)),
ModKind::Alpha(am) => match am {
AlphaMod::InvAlpha(_) => return Err(RuleRuntimeError::GroupSuprIsInverted("stress", err_pos)),
AlphaMod::Alpha(ch) => {
if let Some(alph) = alphas.borrow().get(&ch) {
let Some(group) = alph.as_grouped() else { return Err(RuleRuntimeError::AlphaIsNotSuprGroup(err_pos)) };
match group {
(true, true) => self.stress = StressKind::Secondary,
(true, false) => self.stress = StressKind::Primary,
(false, false) => self.stress = StressKind::Unstressed,
(false, true) => return Err(RuleRuntimeError::SecStrPosStrNeg(err_pos)),
}
} else {
return Err(RuleRuntimeError::AlphaUnknown(err_pos))
}
},
},
},
}
}
if let Some(t) = &mods.tone {
self.tone = *t;
}
Ok(())
}
}
impl PartialEq for Syllable {
fn eq(&self, other: &Self) -> bool {
self.segments == other.segments && self.stress == other.stress && self.tone == other.tone
}
}
impl fmt::Display for Syllable {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "({},{},'{}')", self.segments.len(), self.stress, self.tone)
}
}
#[test]
fn test_seg_indices() {
use super::*;
let indices = (&Word::new("ka:r".to_owned()).unwrap().syllables[0]).seg_indices();
assert_eq!(indices.len(), 3);
assert_eq!(indices[0], 0);
assert_eq!(indices[1], 1);
assert_eq!(indices[2], 3);
let indices = (&Word::new("ka::r".to_owned()).unwrap().syllables[0]).seg_indices();
assert_eq!(indices.len(), 3);
assert_eq!(indices[0], 0);
assert_eq!(indices[1], 1);
assert_eq!(indices[2], 4);
let indices = (&Word::new("k:ar:".to_owned()).unwrap().syllables[0]).seg_indices();
assert_eq!(indices.len(), 3);
assert_eq!(indices[0], 0);
assert_eq!(indices[1], 2);
assert_eq!(indices[2], 3);
}