use crate::lexicon;
use crate::rules;
use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct Tagger {
lexicon: HashMap<String, Vec<String>>,
}
impl Default for Tagger {
fn default() -> Self {
Self::new()
}
}
impl Tagger {
pub fn new() -> Self {
Tagger {
lexicon: lexicon::load(),
}
}
pub fn tag<S: AsRef<str>>(&self, words: &[S]) -> Vec<(String, String)> {
let mut sentence: Vec<(String, String)> = words
.iter()
.map(|word| {
let word = word.as_ref();
(word.to_string(), self.initial_tag(word))
})
.collect();
for index in 0..sentence.len() {
rules::apply_all(&mut sentence, index);
}
sentence
}
fn initial_tag(&self, word: &str) -> String {
if let Some(tags) = self.lexicon.get(word) {
return first_tag(tags);
}
if let Some(tags) = self.lexicon.get(&word.to_lowercase()) {
return first_tag(tags);
}
"NN".to_string()
}
pub fn word_in_lexicon(&self, word: &str) -> bool {
if self.lexicon.contains_key(word) {
return true;
}
self.lexicon.contains_key(&word.to_lowercase())
}
pub fn extend_lexicon<I>(&mut self, entries: I)
where
I: IntoIterator<Item = (String, Vec<String>)>,
{
for (word, tags) in entries {
self.lexicon.entry(word).or_insert(tags);
}
}
}
fn first_tag(tags: &[String]) -> String {
tags.first().cloned().unwrap_or_else(|| "NN".to_string())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn unknown_word_defaults_to_nn() {
let t = Tagger::new();
assert_eq!(t.tag(&["Zzqxxw"]), vec![("Zzqxxw".into(), "NN".into())]);
}
#[test]
fn lowercase_fallback() {
let t = Tagger::new();
assert_eq!(t.tag(&["THE"]), vec![("THE".into(), "DT".into())]);
}
}