use std::convert::AsRef;
use std::fmt::{self, Debug, Display};
use lasso::{Key, Spur, ThreadedRodeo};
use once_cell::sync::OnceCell;
use super::span::Span;
pub use lasso;
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct Symbol(Spur);
impl Symbol {
#[must_use]
pub fn intern(string: &str) -> Self {
Self(get_interner().get_or_intern(string))
}
#[must_use]
pub fn intern_static(string: &'static str) -> Self {
Self(get_interner().get_or_intern_static(string))
}
#[must_use]
#[doc(hidden)]
pub fn intern_static_2(string: &'static str) -> Self {
Self(
GLOBAL_INTERNER
.get_or_init(ThreadedRodeo::new)
.get_or_intern_static(string),
)
}
#[must_use]
#[allow(clippy::cast_possible_truncation)]
pub fn as_u32(self) -> u32 {
self.0.into_usize() as u32
}
#[must_use]
pub fn as_str(self) -> &'static str {
get_interner().resolve(&self.0)
}
#[must_use]
pub fn is_empty(self) -> bool {
self == special::EMPTY
}
#[must_use]
#[cfg(feature = "calypso_interns")]
pub fn is_keyword(self) -> bool {
kw::is(self)
}
}
impl Debug for Symbol {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_str())
}
}
impl Display for Symbol {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_str())
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct Ident {
pub symbol: Symbol,
pub span: Span,
}
impl std::ops::Deref for Ident {
type Target = Symbol;
fn deref(&self) -> &Self::Target {
&self.symbol
}
}
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum PotentiallyInterned<'a> {
Uninterned(&'a str),
Interned(Symbol),
}
impl<'a> Debug for PotentiallyInterned<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_ref())
}
}
impl<'a> Display for PotentiallyInterned<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_ref())
}
}
impl<'a> PotentiallyInterned<'a> {
#[must_use]
pub fn potentially_intern(string: &'a str) -> Self {
if string.len() > 255 {
Self::Uninterned(string)
} else {
Self::Interned(Symbol::intern(string))
}
}
}
impl<'a> AsRef<str> for PotentiallyInterned<'a> {
fn as_ref(&self) -> &str {
match self {
PotentiallyInterned::Interned(sym) => sym.as_str(),
PotentiallyInterned::Uninterned(s) => *s,
}
}
}
static GLOBAL_INTERNER: OnceCell<ThreadedRodeo> = OnceCell::new();
pub fn get_interner() -> &'static ThreadedRodeo {
let int = GLOBAL_INTERNER.get_or_init(ThreadedRodeo::new);
#[cfg(feature = "calypso_interns")]
{
kw::init();
}
special::init();
int
}
macro_rules! intern_static {
($mod:ident, $mod_doc:expr, $name:ident => {$($enum_ident:ident; $static_ident:ident: $str:expr; $doc:expr),*$(,)?}) => {
#[doc = $mod_doc]
#[allow(dead_code)]
pub mod $mod {
::lazy_static::lazy_static! {
$(
#[doc = $doc]
pub static ref $static_ident: $crate::symbol::Symbol
= $crate::symbol::Symbol::intern_static_2($str);
)*
}
pub(super) fn init() {
$(::lazy_static::initialize(&$static_ident);)*
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum $name {
$(
#[doc = $doc]
$enum_ident,
)*
}
impl ::std::convert::TryFrom<$crate::symbol::Symbol> for $name {
type Error = $crate::symbol::Symbol;
fn try_from(sym: $crate::symbol::Symbol) -> Result<Self, Self::Error> {
$(
if sym == $static_ident {
return Ok(Self::$enum_ident);
}
)*
return Err(sym);
}
}
impl From<$name> for $crate::symbol::Symbol {
fn from(elem: $name) -> Self {
$(
if elem == $name::$enum_ident {
return *$static_ident;
}
)*
unreachable!()
}
}
use ::std::ops::Deref;
$(
impl PartialEq<$static_ident> for $crate::symbol::Symbol {
fn eq(&self, symbol: &$static_ident) -> bool {
self == symbol.deref()
}
}
impl PartialEq<$static_ident> for $static_ident {
fn eq(&self, symbol: &$static_ident) -> bool {
self.deref() == symbol.deref()
}
}
impl Eq for $static_ident {}
)*
#[must_use]
pub fn is(sym: $crate::symbol::Symbol) -> bool {
$(
sym == $crate::symbol::Symbol::from($name::$enum_ident)
)||*
}
}
}
}
intern_static! {kw, "Keywords", Keyword => {
True; TRUE: "true"; "True (`true`)",
False; FALSE: "false"; "False (`false`)",
Let; LET: "let"; "Let (`let`)",
Mut; MUT: "mut"; "Mut (`mut`)",
Do; DO: "do"; "Do (`do`)",
End; END: "end"; "End (`end`)",
In; IN: "in"; "In (`in`)",
}}
intern_static! {special, "Special strings", Special => {
Empty; EMPTY: ""; "Empty string"
}}