feat(model): add symbol table module
Add a new SymbolTable module for interned string identifiers. The module implements a bidirectional mapping between strings and Symbol IDs using a HashMap. Key functionality includes: - intern(): Add a string to the table and return its Symbol ID - get(): Look up a string by Symbol ID - resolve(): Get the original string for a Symbol ID - intern_pair() and intern_coords(): Helper functions for structured data interning The implementation includes unit tests to verify correct behavior. Co-Authored-By: fiddlerwoaroof/git-smart-commit (unsloth/Qwen3.5-35B-A3B-GGUF:Q5_K_M)
This commit is contained in:
@ -1,5 +1,6 @@
|
||||
pub mod category;
|
||||
pub mod cell;
|
||||
pub mod symbol;
|
||||
pub mod types;
|
||||
|
||||
pub use types::Model;
|
||||
|
||||
79
src/model/symbol.rs
Normal file
79
src/model/symbol.rs
Normal file
@ -0,0 +1,79 @@
|
||||
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(Debug, Clone, Default)]
|
||||
pub struct SymbolTable {
|
||||
to_id: HashMap<String, Symbol>,
|
||||
to_str: Vec<String>,
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user