talc/talc-lang/src/symbol.rs

122 lines
3.4 KiB
Rust

use std::{collections::HashMap, sync::{Mutex, OnceLock}};
use lazy_static::lazy_static;
#[derive(Default)]
struct SymbolTable {
names: Vec<&'static str>,
values: HashMap<&'static str, Symbol>
}
lazy_static! {
pub static ref SYM_NIL: Symbol = symbol!(nil);
pub static ref SYM_BOOL: Symbol = symbol!(bool);
pub static ref SYM_SYMBOL: Symbol = symbol!(symbol);
pub static ref SYM_INT: Symbol = symbol!(int);
pub static ref SYM_RATIO: Symbol = symbol!(ratio);
pub static ref SYM_FLOAT: Symbol = symbol!(float);
pub static ref SYM_COMPLEX: Symbol = symbol!(complex);
pub static ref SYM_RANGE: Symbol = symbol!(range);
pub static ref SYM_CELL: Symbol = symbol!(cell);
pub static ref SYM_STRING: Symbol = symbol!(string);
pub static ref SYM_LIST: Symbol = symbol!(list);
pub static ref SYM_TABLE: Symbol = symbol!(table);
pub static ref SYM_FUNCTION: Symbol = symbol!(function);
pub static ref SYM_NATIVE_FUNC: Symbol = symbol!(native_func);
pub static ref SYM_END_ITERATION: Symbol = symbol!(end_iteration);
pub static ref SYM_TYPE: Symbol = symbol!(type);
pub static ref SYM_MSG: Symbol = symbol!(msg);
pub static ref SYM_DATA: Symbol = symbol!(data);
pub static ref SYM_TYPE_ERROR: Symbol = symbol!(type_error);
pub static ref SYM_NAME_ERROR: Symbol = symbol!(name_error);
pub static ref SYM_INDEX_ERROR: Symbol = symbol!(index_error);
pub static ref SYM_HASH_ERROR: Symbol = symbol!(hash_error);
pub static ref SYM_CALL_STACK_OVERFLOW: Symbol = symbol!(call_stack_overflow);
pub static ref SYM_INTERRUPTED: Symbol = symbol!(interrupted);
}
static TABLE: OnceLock<Mutex<SymbolTable>> = OnceLock::new();
const MAX_SYMBOL: usize = 0xff_ffff;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
#[repr(transparent)]
pub struct Symbol(u32);
fn get_table() -> &'static Mutex<SymbolTable> {
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 = String::leak(name.to_owned());
table.names.push(name);
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) -> &'static 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")
}
}
#[macro_export]
macro_rules! symbol {
($sym:ident) => {
$crate::symbol::Symbol::get(stringify!($sym))
};
($sym:literal) => {
$crate::symbol::Symbol::get($sym)
};
}
pub use symbol;