logicaffeine_language/
registry.rs1use std::collections::HashMap;
10use logicaffeine_base::{Interner, Symbol};
11
12pub struct SymbolRegistry {
14 mapping: HashMap<String, String>,
15 counters: HashMap<char, usize>,
16}
17
18impl SymbolRegistry {
19 pub fn new() -> Self {
20 SymbolRegistry {
21 mapping: HashMap::new(),
22 counters: HashMap::new(),
23 }
24 }
25
26 pub fn get_symbol_full(&self, sym: Symbol, interner: &Interner) -> String {
27 let word = interner.resolve(sym);
28 let mut chars = word.chars();
29 match chars.next() {
30 Some(c) => c.to_uppercase().collect::<String>() + chars.as_str(),
31 None => String::new(),
32 }
33 }
34
35 pub fn get_symbol(&mut self, sym: Symbol, interner: &Interner) -> String {
36 let word = interner.resolve(sym);
37 let normalized = word.to_lowercase();
38
39 if let Some(sym) = self.mapping.get(&normalized) {
40 return sym.clone();
41 }
42
43 if word.contains('-') {
46 let compound: String = word
47 .split('-')
48 .map(|part| {
49 let mut chars = part.chars();
50 match chars.next() {
51 Some(c) => c.to_uppercase().collect::<String>() + chars.as_str(),
52 None => String::new(),
53 }
54 })
55 .collect::<Vec<_>>()
56 .join("-");
57 self.mapping.insert(normalized, compound.clone());
58 return compound;
59 }
60
61 const PRESERVED_TERMS: &[&str] = &["PartOf"];
63 if PRESERVED_TERMS.iter().any(|t| t.eq_ignore_ascii_case(word)) {
64 self.mapping.insert(normalized, word.to_string());
65 return word.to_string();
66 }
67
68 let first = normalized
69 .chars()
70 .next()
71 .unwrap()
72 .to_uppercase()
73 .next()
74 .unwrap();
75
76 let counter = self.counters.entry(first).or_insert(0);
77 *counter += 1;
78
79 let symbol = if *counter == 1 {
80 first.to_string()
81 } else {
82 format!("{}{}", first, counter)
83 };
84
85 self.mapping.insert(normalized, symbol.clone());
86 symbol
87 }
88}
89
90impl Default for SymbolRegistry {
91 fn default() -> Self {
92 Self::new()
93 }
94}
95
96#[cfg(test)]
97mod tests {
98 use super::*;
99
100 #[test]
101 fn first_word_gets_single_letter() {
102 let mut interner = Interner::new();
103 let mut reg = SymbolRegistry::new();
104 let dog = interner.intern("dog");
105 assert_eq!(reg.get_symbol(dog, &interner), "D");
106 }
107
108 #[test]
109 fn second_word_same_letter_gets_numbered() {
110 let mut interner = Interner::new();
111 let mut reg = SymbolRegistry::new();
112 let dog = interner.intern("dog");
113 let dangerous = interner.intern("dangerous");
114 reg.get_symbol(dog, &interner);
115 assert_eq!(reg.get_symbol(dangerous, &interner), "D2");
116 }
117
118 #[test]
119 fn same_word_returns_same_symbol() {
120 let mut interner = Interner::new();
121 let mut reg = SymbolRegistry::new();
122 let cat = interner.intern("cat");
123 let first = reg.get_symbol(cat, &interner);
124 let second = reg.get_symbol(cat, &interner);
125 assert_eq!(first, second);
126 }
127
128 #[test]
129 fn case_insensitive() {
130 let mut interner = Interner::new();
131 let mut reg = SymbolRegistry::new();
132 let dog = interner.intern("dog");
133 let dog_upper = interner.intern("DOG");
134 let lower = reg.get_symbol(dog, &interner);
135 let upper = reg.get_symbol(dog_upper, &interner);
136 assert_eq!(lower, upper);
137 }
138}