127 lines
3.5 KiB
Rust
127 lines
3.5 KiB
Rust
use std::{collections::HashMap, sync::{Mutex, OnceLock}};
|
|
|
|
use lazy_static::lazy_static;
|
|
|
|
#[derive(Default)]
|
|
struct SymbolTable {
|
|
names: Vec<&'static LStr>,
|
|
values: HashMap<&'static LStr, 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<'a, S: Into<&'a LStr>>(name: S) -> Option<Self> {
|
|
let name = name.into();
|
|
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<'a, S: Into<&'a LStr>>(name: S) -> Self {
|
|
let name = name.into();
|
|
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 = LString::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 LStr {
|
|
let table = get_table().lock().expect("couldn't lock symbol table");
|
|
table.names.get(self.0 as usize).copied().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;
|
|
|
|
use crate::lstring::{LStr, LString};
|
|
|