Files
improvise/crates/improvise-core/src/model/symbol.rs
T

90 lines
2.5 KiB
Rust

use std::collections::HashMap;
/// An interned string identifier. Copy-cheap, O(1) hash and equality.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Symbol(u64);
/// Bidirectional string ↔ Symbol mapping.
#[derive(Clone, Default)]
pub struct SymbolTable {
to_id: HashMap<String, Symbol>,
to_str: Vec<String>,
}
/// The interned strings, sorted: which `Symbol` each got is interning order,
/// not content, so two equal stores print alike (improvise-djcn).
impl std::fmt::Debug for SymbolTable {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let SymbolTable { to_id, to_str: _ } = self;
let interned: std::collections::BTreeSet<&String> = to_id.keys().collect();
f.debug_set().entries(interned).finish()
}
}
impl SymbolTable {
#[allow(dead_code)]
pub fn new() -> Self {
Self::default()
}
/// Intern a string, returning its Symbol. Returns existing Symbol if
/// already interned.
pub fn intern(&mut self, s: &str) -> Symbol {
if let Some(&id) = self.to_id.get(s) {
return id;
}
let id = Symbol(self.to_str.len() as u64);
self.to_str.push(s.to_string());
self.to_id.insert(s.to_string(), id);
id
}
/// Look up the Symbol for a string without interning.
pub fn get(&self, s: &str) -> Option<Symbol> {
self.to_id.get(s).copied()
}
/// Resolve a Symbol back to its string.
pub fn resolve(&self, sym: Symbol) -> &str {
&self.to_str[sym.0 as usize]
}
/// Intern a (category, item) pair.
pub fn intern_pair(&mut self, cat: &str, item: &str) -> (Symbol, Symbol) {
(self.intern(cat), self.intern(item))
}
/// Intern a full coordinate list.
pub fn intern_coords(&mut self, coords: &[(String, String)]) -> Vec<(Symbol, Symbol)> {
coords.iter().map(|(c, i)| self.intern_pair(c, i)).collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn intern_returns_same_id() {
let mut t = SymbolTable::new();
let a = t.intern("hello");
let b = t.intern("hello");
assert_eq!(a, b);
}
#[test]
fn different_strings_different_ids() {
let mut t = SymbolTable::new();
let a = t.intern("hello");
let b = t.intern("world");
assert_ne!(a, b);
}
#[test]
fn resolve_roundtrips() {
let mut t = SymbolTable::new();
let s = t.intern("test");
assert_eq!(t.resolve(s), "test");
}
}