use crate::lemmatizer::Lemmatizer;
const ZWNJ: char = '\u{200C}';
pub struct TokenSplitter {
lemmatizer: Lemmatizer,
}
impl TokenSplitter {
pub fn new() -> Self {
Self {
lemmatizer: Lemmatizer::new(),
}
}
pub fn split(&self, token: &str) -> Vec<Vec<String>> {
let mut candidates: Vec<Vec<String>> = Vec::new();
if token.contains(ZWNJ) {
let parts: Vec<String> = token
.split(ZWNJ)
.map(str::to_string)
.collect();
if self.all_in_lexicon(&parts) {
candidates.push(parts);
}
}
let chars: Vec<char> = token.chars().collect();
let n = chars.len();
for split_at in 1..n {
if chars[split_at - 1] == ZWNJ || chars[split_at] == ZWNJ {
continue;
}
let left: String = chars[..split_at].iter().collect();
let right: String = chars[split_at..].iter().collect();
let pair = vec![left, right];
if self.all_in_lexicon(&pair) {
candidates.push(pair);
}
}
let whole = vec![token.to_string()];
if self.all_in_lexicon(&whole) && !candidates.iter().any(|c| c == &whole) {
candidates.push(whole);
}
candidates
}
fn all_in_lexicon(&self, parts: &[String]) -> bool {
parts.iter().all(|p| {
let lemma = self.lemmatizer.lemmatize(p, "");
self.lemmatizer.words.contains_key(&lemma)
|| self.lemmatizer.words.contains_key(p.as_str())
})
}
}
impl Default for TokenSplitter {
fn default() -> Self {
Self::new()
}
}