talc/talc-lang/src/symbol.rs

77 lines
1.8 KiB
Rust

use std::{sync::{OnceLock, Arc, Mutex}, collections::HashMap};
#[derive(Default)]
struct SymbolTable {
names: Vec<Arc<str>>,
values: HashMap<Arc<str>, Symbol>
}
static SYM_TABLE: OnceLock<Mutex<SymbolTable>> = OnceLock::new();
const MAX_SYMBOL: usize = 0xff_ffff;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
pub struct Symbol(u32);
fn get_table() -> &'static Mutex<SymbolTable> {
SYM_TABLE.get_or_init(|| Mutex::new(SymbolTable::default()))
}
impl Symbol {
/// # Panics
/// If the mutex storing the symbol table is poisoned
pub fn try_get(name: &str) -> Option<Self> {
let table = get_table().lock().expect("couldn't lock symbol table");
table.values.get(name).copied()
}
/// # Panics
/// If the mutex storing the symbol table is poisoned
pub fn get(name: &str) -> Self {
let mut table = get_table().lock().expect("couldn't lock symbol table");
if let Some(sym) = table.values.get(name) {
return *sym
}
let symno = table.names.len();
assert!(symno <= MAX_SYMBOL, "too many symbols");
let sym = Symbol(symno as u32);
let name: Arc<str> = name.into();
table.names.push(name.clone());
table.values.insert(name, sym);
sym
}
/// # Panics
/// If the mutex storing the symbol table is poisoned
pub fn from_id(id: u32) -> Option<Self> {
let table = get_table().lock().expect("couldn't lock symbol table");
if id as usize <= table.names.len() {
Some(Self(id))
} else {
None
}
}
/// # Safety
/// Safe if argument is a valid symbol ID
pub unsafe fn from_id_unchecked(id: u32) -> Self {
Self(id)
}
pub fn id(self) -> u32 {
self.0
}
/// # Panics
/// If the mutex storing the symbol table is poisoned
pub fn name(self) -> Arc<str> {
let table = get_table().lock().expect("couldn't lock symbol table");
table.names.get(self.0 as usize).cloned().expect("symbol does not exist")
}
}