use crate::{SymbolId, SymbolMetadata};
use indexmap::IndexMap;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SymbolRegistry {
symbols: IndexMap<String, SymbolId>,
ids: HashMap<SymbolId, String>,
metadata: HashMap<SymbolId, SymbolMetadata>,
next_id: u16,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SymbolInfo {
pub id: SymbolId,
pub metadata: SymbolMetadata,
}
impl SymbolRegistry {
pub fn new() -> Self {
let mut registry = Self {
symbols: IndexMap::new(),
ids: HashMap::new(),
metadata: HashMap::new(),
next_id: 0,
};
registry.register(
"end",
SymbolMetadata {
visible: true,
named: false,
hidden: false,
terminal: true,
},
);
registry
}
pub fn register(&mut self, name: &str, metadata: SymbolMetadata) -> SymbolId {
if let Some(&id) = self.symbols.get(name) {
self.metadata.insert(id, metadata);
return id;
}
let id = SymbolId(self.next_id);
self.next_id += 1;
self.symbols.insert(name.to_string(), id);
self.ids.insert(id, name.to_string());
self.metadata.insert(id, metadata);
id
}
pub fn get_id(&self, name: &str) -> Option<SymbolId> {
self.symbols.get(name).copied()
}
pub fn get_name(&self, id: SymbolId) -> Option<&str> {
self.ids.get(&id).map(String::as_str)
}
pub fn get_metadata(&self, id: SymbolId) -> Option<SymbolMetadata> {
self.metadata.get(&id).copied()
}
pub fn contains_id(&self, id: SymbolId) -> bool {
self.ids.contains_key(&id)
}
pub fn len(&self) -> usize {
self.symbols.len()
}
pub fn is_empty(&self) -> bool {
self.symbols.is_empty()
}
pub fn iter(&self) -> impl Iterator<Item = (&str, SymbolInfo)> {
self.symbols.iter().map(move |(name, &id)| {
let metadata = self.metadata[&id];
(name.as_str(), SymbolInfo { id, metadata })
})
}
pub fn to_index_map(&self) -> HashMap<SymbolId, usize> {
self.symbols
.values()
.enumerate()
.map(|(idx, &id)| (id, idx))
.collect()
}
pub fn to_symbol_map(&self) -> HashMap<usize, SymbolId> {
self.symbols
.values()
.enumerate()
.map(|(idx, &id)| (idx, id))
.collect()
}
}
impl Default for SymbolRegistry {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_registry_deterministic() {
let mut reg1 = SymbolRegistry::new();
let mut reg2 = SymbolRegistry::new();
for name in ["number", "plus", "minus", "expr"] {
let meta = SymbolMetadata {
visible: true,
named: name == "expr",
hidden: false,
terminal: name != "expr",
};
reg1.register(name, meta);
reg2.register(name, meta);
}
for name in ["number", "plus", "minus", "expr"] {
assert_eq!(reg1.get_id(name), reg2.get_id(name));
}
}
#[test]
fn test_eof_is_zero() {
let registry = SymbolRegistry::new();
assert_eq!(registry.get_id("end"), Some(SymbolId(0)));
}
}