use crate::symbol::SymbolId;
use slotmap::{new_key_type, SecondaryMap, SlotMap};
new_key_type! {
pub struct VarId;
}
#[derive(Debug, Clone, Default)]
pub struct VarSymbolMapping {
var_to_symbol: SlotMap<VarId, Option<SymbolId>>,
symbol_to_var: SecondaryMap<SymbolId, VarId>,
}
impl VarSymbolMapping {
pub fn new() -> Self {
Self::default()
}
pub fn with_capacity(capacity: usize) -> Self {
Self {
var_to_symbol: SlotMap::with_capacity_and_key(capacity),
symbol_to_var: SecondaryMap::with_capacity(capacity),
}
}
pub fn register(&mut self, symbol_id: SymbolId) -> VarId {
if let Some(&var_id) = self.symbol_to_var.get(symbol_id) {
if self.var_to_symbol.contains_key(var_id) {
return var_id;
}
}
let var_id = self.var_to_symbol.insert(Some(symbol_id));
self.symbol_to_var.insert(symbol_id, var_id);
var_id
}
pub fn allocate(&mut self) -> VarId {
self.var_to_symbol.insert(None)
}
pub fn remove(&mut self, var_id: VarId) -> Option<SymbolId> {
if let Some(Some(symbol_id)) = self.var_to_symbol.remove(var_id) {
self.symbol_to_var.remove(symbol_id);
return Some(symbol_id);
}
None
}
pub fn remove_by_symbol(&mut self, symbol_id: SymbolId) -> Option<VarId> {
if let Some(var_id) = self.symbol_to_var.remove(symbol_id) {
self.var_to_symbol.remove(var_id);
Some(var_id)
} else {
None
}
}
#[inline]
pub fn to_symbol(&self, var_id: VarId) -> Option<SymbolId> {
self.var_to_symbol.get(var_id).copied().flatten()
}
#[inline]
pub fn to_var(&self, symbol_id: SymbolId) -> Option<VarId> {
self.symbol_to_var.get(symbol_id).copied()
}
#[inline]
pub fn contains_symbol(&self, symbol_id: SymbolId) -> bool {
self.symbol_to_var.contains_key(symbol_id)
}
#[inline]
pub fn contains_var(&self, var_id: VarId) -> bool {
self.var_to_symbol.contains_key(var_id)
}
#[inline]
pub fn len(&self) -> usize {
self.var_to_symbol.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.var_to_symbol.is_empty()
}
pub fn iter(&self) -> impl Iterator<Item = (VarId, Option<SymbolId>)> + '_ {
self.var_to_symbol.iter().map(|(id, &sym)| (id, sym))
}
pub fn clear(&mut self) {
self.var_to_symbol.clear();
self.symbol_to_var.clear();
}
}
#[cfg(test)]
mod tests {
use super::*;
use slotmap::SlotMap;
#[test]
fn test_var_symbol_mapping_register() {
let mut symbols: SlotMap<SymbolId, &str> = SlotMap::with_key();
let sym1 = symbols.insert("x");
let sym2 = symbols.insert("y");
let mut mapping = VarSymbolMapping::new();
let var1 = mapping.register(sym1);
let var2 = mapping.register(sym2);
assert_ne!(var1, var2);
assert_eq!(mapping.len(), 2);
}
#[test]
fn test_var_symbol_mapping_idempotent() {
let mut symbols: SlotMap<SymbolId, &str> = SlotMap::with_key();
let sym = symbols.insert("x");
let mut mapping = VarSymbolMapping::new();
let var1 = mapping.register(sym);
let var2 = mapping.register(sym);
assert_eq!(var1, var2);
assert_eq!(mapping.len(), 1);
}
#[test]
fn test_var_symbol_mapping_bidirectional() {
let mut symbols: SlotMap<SymbolId, &str> = SlotMap::with_key();
let sym = symbols.insert("x");
let mut mapping = VarSymbolMapping::new();
let var = mapping.register(sym);
assert_eq!(mapping.to_symbol(var), Some(sym));
assert_eq!(mapping.to_var(sym), Some(var));
}
#[test]
fn test_var_symbol_mapping_contains() {
let mut symbols: SlotMap<SymbolId, &str> = SlotMap::with_key();
let sym1 = symbols.insert("x");
let sym2 = symbols.insert("y");
let mut mapping = VarSymbolMapping::new();
let var = mapping.register(sym1);
assert!(mapping.contains_symbol(sym1));
assert!(!mapping.contains_symbol(sym2));
assert!(mapping.contains_var(var));
}
#[test]
fn test_var_symbol_mapping_remove() {
let mut symbols: SlotMap<SymbolId, &str> = SlotMap::with_key();
let sym = symbols.insert("x");
let mut mapping = VarSymbolMapping::new();
let var = mapping.register(sym);
assert_eq!(mapping.len(), 1);
assert!(mapping.contains_var(var));
let removed = mapping.remove(var);
assert_eq!(removed, Some(sym));
assert_eq!(mapping.len(), 0);
assert!(!mapping.contains_var(var));
assert!(!mapping.contains_symbol(sym));
}
#[test]
fn test_var_symbol_mapping_remove_by_symbol() {
let mut symbols: SlotMap<SymbolId, &str> = SlotMap::with_key();
let sym = symbols.insert("x");
let mut mapping = VarSymbolMapping::new();
let var = mapping.register(sym);
let removed = mapping.remove_by_symbol(sym);
assert_eq!(removed, Some(var));
assert_eq!(mapping.len(), 0);
}
#[test]
fn test_var_symbol_mapping_iter() {
let mut symbols: SlotMap<SymbolId, &str> = SlotMap::with_key();
let sym1 = symbols.insert("x");
let sym2 = symbols.insert("y");
let mut mapping = VarSymbolMapping::new();
mapping.register(sym1);
mapping.register(sym2);
let pairs: Vec<_> = mapping.iter().collect();
assert_eq!(pairs.len(), 2);
}
#[test]
fn test_var_symbol_mapping_clear() {
let mut symbols: SlotMap<SymbolId, &str> = SlotMap::with_key();
let sym = symbols.insert("x");
let mut mapping = VarSymbolMapping::new();
mapping.register(sym);
assert!(!mapping.is_empty());
mapping.clear();
assert!(mapping.is_empty());
assert!(!mapping.contains_symbol(sym));
}
#[test]
fn test_var_symbol_mapping_reregister_after_remove() {
let mut symbols: SlotMap<SymbolId, &str> = SlotMap::with_key();
let sym = symbols.insert("x");
let mut mapping = VarSymbolMapping::new();
let var1 = mapping.register(sym);
mapping.remove(var1);
let var2 = mapping.register(sym);
assert_ne!(var1, var2); assert_eq!(mapping.len(), 1);
assert!(mapping.contains_var(var2));
}
#[test]
fn test_allocate_produces_distinct_ids() {
let mut mapping = VarSymbolMapping::new();
let v1 = mapping.allocate();
let v2 = mapping.allocate();
let v3 = mapping.allocate();
assert_ne!(v1, v2);
assert_ne!(v2, v3);
assert_ne!(v1, v3);
assert_eq!(mapping.len(), 3);
assert_eq!(mapping.to_symbol(v1), None);
assert_eq!(mapping.to_symbol(v2), None);
}
#[test]
fn test_allocate_and_register_coexist() {
let mut symbols: SlotMap<SymbolId, &str> = SlotMap::with_key();
let sym = symbols.insert("x");
let mut mapping = VarSymbolMapping::new();
let anon = mapping.allocate();
let named = mapping.register(sym);
assert_ne!(anon, named);
assert_eq!(mapping.len(), 2);
assert_eq!(mapping.to_symbol(anon), None);
assert_eq!(mapping.to_symbol(named), Some(sym));
assert_eq!(mapping.to_var(sym), Some(named));
}
#[test]
fn test_remove_allocated_var() {
let mut mapping = VarSymbolMapping::new();
let v = mapping.allocate();
assert_eq!(mapping.len(), 1);
let removed = mapping.remove(v);
assert_eq!(removed, None); assert_eq!(mapping.len(), 0);
assert!(!mapping.contains_var(v));
}
}