use std::collections::{HashMap, HashSet};
pub(super) struct LexicalMap {
symbol_map: Vec<HashMap<String, String>>,
reserved_symbols: HashSet<String>,
}
impl LexicalMap {
pub(super) fn from_iter<I: IntoIterator<Item = String>>(names: I) -> Self {
let (root_symbol_map, reserved_symbols): (HashMap<String, String>, HashSet<String>) = names
.into_iter()
.fold((HashMap::new(), HashSet::new()), |(mut m, mut r), name| {
m.insert(name.clone(), name.clone());
r.insert(name);
(m, r)
});
LexicalMap {
symbol_map: vec![root_symbol_map],
reserved_symbols,
}
}
pub(super) fn enter_scope(&mut self) -> &mut Self {
self.symbol_map.push(HashMap::new());
self
}
pub(super) fn leave_scope(&mut self) -> &mut Self {
assert!(self.symbol_map.len() > 1);
self.symbol_map.pop();
self
}
pub(super) fn get(&self, symbol: &str) -> Option<&String> {
self.symbol_map
.iter()
.rev()
.find_map(|scope| scope.get(symbol))
}
pub(super) fn insert(&mut self, new_symbol: String) -> String {
fn get_new_local_symbol(reserved: &HashSet<String>, candidate: String) -> String {
if reserved.contains(&candidate) {
get_new_local_symbol(reserved, format!("{candidate}_"))
} else {
candidate
}
}
let local_symbol = get_new_local_symbol(&self.reserved_symbols, new_symbol.clone());
self.symbol_map
.last_mut()
.expect("LexicalMap should always have at least the root scope.")
.insert(new_symbol, local_symbol.clone());
self.reserved_symbols.insert(local_symbol.clone());
local_symbol
}
pub(super) fn insert_anon(&mut self) -> String {
self.insert_unique_named("anon")
}
pub(super) fn insert_unique_named(&mut self, name: &str) -> String {
let anon_symbol = (0..)
.map(|n| format!("__{name}_{n}"))
.find(|candidate| !self.reserved_symbols.contains(candidate))
.unwrap();
self.symbol_map
.last_mut()
.expect("LexicalMap should always have at least the root scope.")
.insert(anon_symbol.clone(), anon_symbol.clone());
self.reserved_symbols.insert(anon_symbol.clone());
anon_symbol
}
}