#![warn(missing_docs)]
use parking_lot::RwLock;
use rustc_hash::FxHashMap;
use serde::{Deserialize, Serialize};
use std::fmt;
use std::sync::LazyLock;
static INTERNER: LazyLock<Interner> = LazyLock::new(Interner::new);
#[derive(Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(transparent)]
pub struct Symbol(u32);
impl Symbol {
#[must_use]
pub fn intern(s: &str) -> Self {
INTERNER.intern(s)
}
#[must_use]
pub fn as_str(self) -> &'static str {
INTERNER.get(self)
}
#[must_use]
pub const fn as_u32(self) -> u32 {
self.0
}
#[must_use]
pub const unsafe fn from_raw(idx: u32) -> Self {
Self(idx)
}
#[must_use]
pub fn is_empty(self) -> bool {
self.as_str().is_empty()
}
#[must_use]
pub fn len(self) -> usize {
self.as_str().len()
}
}
impl fmt::Debug for Symbol {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Symbol({:?})", self.as_str())
}
}
impl fmt::Display for Symbol {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_str())
}
}
impl From<&str> for Symbol {
fn from(s: &str) -> Self {
Self::intern(s)
}
}
impl From<String> for Symbol {
fn from(s: String) -> Self {
Self::intern(&s)
}
}
impl AsRef<str> for Symbol {
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl PartialEq<str> for Symbol {
fn eq(&self, other: &str) -> bool {
self.as_str() == other
}
}
impl PartialEq<&str> for Symbol {
fn eq(&self, other: &&str) -> bool {
self.as_str() == *other
}
}
impl PartialOrd for Symbol {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Symbol {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.as_str().cmp(other.as_str())
}
}
struct Interner {
map: RwLock<FxHashMap<&'static str, Symbol>>,
strings: RwLock<Vec<&'static str>>,
}
impl Interner {
fn new() -> Self {
Self {
map: RwLock::new(FxHashMap::default()),
strings: RwLock::new(Vec::new()),
}
}
fn intern(&self, s: &str) -> Symbol {
{
let map = self.map.read();
if let Some(&sym) = map.get(s) {
return sym;
}
}
let mut map = self.map.write();
let mut strings = self.strings.write();
if let Some(&sym) = map.get(s) {
return sym;
}
let interned: &'static str = Box::leak(s.to_string().into_boxed_str());
let sym = Symbol(strings.len() as u32);
strings.push(interned);
map.insert(interned, sym);
sym
}
fn get(&self, sym: Symbol) -> &'static str {
let strings = self.strings.read();
strings[sym.0 as usize]
}
}
pub mod kw {
use super::Symbol;
use std::sync::LazyLock;
macro_rules! define_keywords {
($($name:ident => $string:literal),* $(,)?) => {
$(
#[doc = concat!("The `", $string, "` keyword.")]
pub static $name: LazyLock<Symbol> = LazyLock::new(|| Symbol::intern($string));
)*
pub fn intern_all() {
$(
let _ = *$name;
)*
}
};
}
define_keywords! {
CASE => "case",
CLASS => "class",
DATA => "data",
DEFAULT => "default",
DERIVING => "deriving",
DO => "do",
ELSE => "else",
FORALL => "forall",
FOREIGN => "foreign",
IF => "if",
IMPORT => "import",
IN => "in",
INFIX => "infix",
INFIXL => "infixl",
INFIXR => "infixr",
INSTANCE => "instance",
LET => "let",
MODULE => "module",
NEWTYPE => "newtype",
OF => "of",
QUALIFIED => "qualified",
THEN => "then",
TYPE => "type",
WHERE => "where",
LAZY => "lazy",
STRICT => "strict",
PROFILE => "profile",
EDITION => "edition",
INT => "Int",
FLOAT => "Float",
DOUBLE => "Double",
BOOL => "Bool",
CHAR => "Char",
STRING => "String",
UNIT => "()",
TRUE => "True",
FALSE => "False",
JUST => "Just",
NOTHING => "Nothing",
LEFT => "Left",
RIGHT => "Right",
UNDERSCORE => "_",
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Ident {
pub name: Symbol,
}
impl Ident {
#[must_use]
pub fn new(name: Symbol) -> Self {
Self { name }
}
#[must_use]
pub fn from_str(s: &str) -> Self {
Self {
name: Symbol::intern(s),
}
}
#[must_use]
pub fn as_str(self) -> &'static str {
self.name.as_str()
}
}
impl fmt::Debug for Ident {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Ident({:?})", self.name.as_str())
}
}
impl fmt::Display for Ident {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.name.as_str())
}
}
impl From<&str> for Ident {
fn from(s: &str) -> Self {
Self::from_str(s)
}
}
impl From<Symbol> for Ident {
fn from(name: Symbol) -> Self {
Self::new(name)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_symbol_interning() {
let s1 = Symbol::intern("hello");
let s2 = Symbol::intern("hello");
let s3 = Symbol::intern("world");
assert_eq!(s1, s2);
assert_ne!(s1, s3);
assert_eq!(s1.as_str(), "hello");
}
#[test]
fn test_symbol_comparison() {
let s1 = Symbol::intern("apple");
let s2 = Symbol::intern("banana");
assert!(s1 < s2);
assert_eq!(s1, "apple");
}
#[test]
fn test_ident() {
let id = Ident::from_str("foo");
assert_eq!(id.as_str(), "foo");
}
}