use std::{
fmt,
sync::{Arc, LazyLock, RwLock},
};
use indexmap::IndexSet;
use rand::distr::{Alphabetic, SampleString};
use scheme_rs_macros::{Trace, bridge};
use crate::{exceptions::Exception, strings::WideString, value::Value};
#[derive(Copy, Clone, PartialEq, Eq, Hash, Trace)]
pub struct Symbol(pub(crate) u32);
static SYMTAB: LazyLock<RwLock<IndexSet<Arc<str>>>> =
LazyLock::new(|| RwLock::new(IndexSet::new()));
impl Symbol {
pub fn intern(s: &str) -> Self {
let mut symtab = SYMTAB.write().unwrap();
let s = Arc::from(s.to_string());
let id = if let Some(id) = symtab.get_index_of(&s) {
id
} else {
let (id, _) = symtab.insert_full(s);
id
};
Self(id.try_into().unwrap())
}
pub fn to_str(self) -> Arc<str> {
let symtab = SYMTAB.read().unwrap();
symtab[self.0 as usize].clone()
}
pub fn print(self) -> String {
self.to_string()
}
pub fn gensym() -> Self {
let string = Alphabetic.sample_string(&mut rand::rng(), 32);
Self::intern(&string)
}
}
impl fmt::Display for Symbol {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.to_str())
}
}
impl fmt::Debug for Symbol {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.to_str())
}
}
impl PartialEq<&'_ str> for Symbol {
fn eq(&self, rhs: &&str) -> bool {
self.to_str().as_ref() == *rhs
}
}
#[bridge(name = "symbol=?", lib = "(rnrs base builtins (6))")]
pub fn symbol_equal_pred(
symbol1: Symbol,
symbol2: Symbol,
symboln: &[Value],
) -> Result<Vec<Value>, Exception> {
if symbol1 != symbol2 {
return Ok(vec![Value::from(false)]);
}
for symboln in symboln {
let symboln = symboln.try_to_scheme_type::<Symbol>()?;
if symbol1 != symboln {
return Ok(vec![Value::from(false)]);
}
}
Ok(vec![Value::from(true)])
}
#[bridge(name = "string->symbol", lib = "(rnrs base builtins (6))")]
pub fn string_to_symbol(s: &Value) -> Result<Vec<Value>, Exception> {
let s: WideString = s.clone().try_into()?;
Ok(vec![Value::from(Symbol::intern(&s.to_string()))])
}
#[bridge(name = "symbol->string", lib = "(rnrs base builtins (6))")]
pub fn symbol_to_string(s: &Value) -> Result<Vec<Value>, Exception> {
let sym: Symbol = s.clone().try_into()?;
Ok(vec![Value::from(sym.to_str().to_string())])
}
#[bridge(name = "gensym", lib = "(rnrs base builtins (6))")]
pub fn gensym() -> Result<Vec<Value>, Exception> {
Ok(vec![Value::from(Symbol::gensym())])
}