use crate::hygiene::SyntaxContext;
use std::cell::RefCell;
use std::collections::HashMap;
use std::fmt;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct Ident {
pub name: Symbol,
pub ctxt: SyntaxContext,
}
impl Ident {
pub fn with_empty_ctxt(name: Symbol) -> Ident {
Ident {
name,
ctxt: SyntaxContext::empty(),
}
}
#[allow(clippy::should_implement_trait)]
pub fn from_str(string: &str) -> Ident {
Ident::with_empty_ctxt(Symbol::intern(string))
}
pub fn modern(self) -> Ident {
Ident {
name: self.name,
ctxt: self.ctxt.modern(),
}
}
}
impl fmt::Debug for Ident {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}{:?}", self.name, self.ctxt)
}
}
impl fmt::Display for Ident {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(&self.name, f)
}
}
impl Serialize for Ident {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
if self.ctxt.modern() == SyntaxContext::empty() {
serializer.serialize_str(&self.name.as_str())
} else {
let mut string = "#".to_owned();
string.push_str(&self.name.as_str());
serializer.serialize_str(&string)
}
}
}
impl<'de> Deserialize<'de> for Ident {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let string = String::deserialize(deserializer)?;
Ok(if !string.starts_with('#') {
Ident::from_str(&string)
} else {
Ident::with_empty_ctxt(Symbol::gensym(&string[1..]))
})
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Symbol(u32);
impl Symbol {
pub fn intern(string: &str) -> Self {
with_interner(|interner| interner.intern(string))
}
pub fn interned(self) -> Self {
with_interner(|interner| interner.interned(self))
}
pub fn gensym(string: &str) -> Self {
with_interner(|interner| interner.gensym(string))
}
pub fn gensymed(self) -> Self {
with_interner(|interner| interner.gensymed(self))
}
pub fn as_str(self) -> InternedString {
with_interner(|interner| unsafe {
InternedString {
string: ::std::mem::transmute::<&str, &str>(interner.get(self)),
}
})
}
pub fn as_u32(self) -> u32 {
self.0
}
}
impl fmt::Debug for Symbol {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}({})", self, self.0)
}
}
impl fmt::Display for Symbol {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(&self.as_str(), f)
}
}
impl Serialize for Symbol {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.as_str())
}
}
impl<'de> Deserialize<'de> for Symbol {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
String::deserialize(deserializer).map(|s| Symbol::intern(&s))
}
}
impl<T: ::std::ops::Deref<Target = str>> PartialEq<T> for Symbol {
fn eq(&self, other: &T) -> bool {
self.as_str() == other.deref()
}
}
#[derive(Default)]
pub struct Interner {
names: HashMap<Box<str>, Symbol>,
strings: Vec<Box<str>>,
gensyms: Vec<Symbol>,
}
impl Interner {
pub fn new() -> Self {
Interner::default()
}
fn prefill(init: &[&str]) -> Self {
let mut this = Interner::new();
for &string in init {
this.intern(string);
}
this
}
pub fn intern(&mut self, string: &str) -> Symbol {
if let Some(&name) = self.names.get(string) {
return name;
}
let name = Symbol(self.strings.len() as u32);
let string = string.to_string().into_boxed_str();
self.strings.push(string.clone());
self.names.insert(string, name);
name
}
pub fn interned(&self, symbol: Symbol) -> Symbol {
if (symbol.0 as usize) < self.strings.len() {
symbol
} else {
self.interned(self.gensyms[(!0 - symbol.0) as usize])
}
}
fn gensym(&mut self, string: &str) -> Symbol {
let symbol = self.intern(string);
self.gensymed(symbol)
}
fn gensymed(&mut self, symbol: Symbol) -> Symbol {
self.gensyms.push(symbol);
Symbol(!0 - self.gensyms.len() as u32 + 1)
}
pub fn get(&self, symbol: Symbol) -> &str {
match self.strings.get(symbol.0 as usize) {
Some(ref string) => string,
None => self.get(self.gensyms[(!0 - symbol.0) as usize]),
}
}
}
macro_rules! declare_keywords {(
$( ($index: expr, $konst: ident, $string: expr) )*
) => {
pub mod keywords {
use super::{Symbol, Ident};
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct Keyword {
ident: Ident,
}
impl Keyword {
#[inline] pub fn ident(self) -> Ident { self.ident }
#[inline] pub fn name(self) -> Symbol { self.ident.name }
}
$(
#[allow(non_upper_case_globals)]
pub const $konst: Keyword = Keyword {
ident: Ident {
name: super::Symbol($index),
ctxt: crate::NO_EXPANSION,
}
};
)*
}
impl Interner {
fn fresh() -> Self {
Interner::prefill(&[$($string,)*])
}
}
}}
declare_keywords! {
(0, Invalid, "")
(1, As, "as")
(2, Box, "box")
(3, Break, "break")
(4, Const, "const")
(5, Continue, "continue")
(6, Crate, "crate")
(7, Else, "else")
(8, Enum, "enum")
(9, Extern, "extern")
(10, False, "false")
(11, Fn, "fn")
(12, For, "for")
(13, If, "if")
(14, Impl, "impl")
(15, In, "in")
(16, Let, "let")
(17, Loop, "loop")
(18, Match, "match")
(19, Mod, "mod")
(20, Move, "move")
(21, Mut, "mut")
(22, Pub, "pub")
(23, Ref, "ref")
(24, Return, "return")
(25, SelfValue, "self")
(26, SelfType, "Self")
(27, Static, "static")
(28, Struct, "struct")
(29, Super, "super")
(30, Trait, "trait")
(31, True, "true")
(32, Type, "type")
(33, Unsafe, "unsafe")
(34, Use, "use")
(35, Where, "where")
(36, While, "while")
(37, Abstract, "abstract")
(38, Alignof, "alignof")
(39, Become, "become")
(40, Do, "do")
(41, Final, "final")
(42, Macro, "macro")
(43, Offsetof, "offsetof")
(44, Override, "override")
(45, Priv, "priv")
(46, Proc, "proc")
(47, Pure, "pure")
(48, Sizeof, "sizeof")
(49, Typeof, "typeof")
(50, Unsized, "unsized")
(51, Virtual, "virtual")
(52, Yield, "yield")
(53, Default, "default")
(54, StaticLifetime, "'static")
(55, Union, "union")
(56, Catch, "catch")
(57, CrateRoot, "{{root}}")
}
fn with_interner<T, F: FnOnce(&mut Interner) -> T>(f: F) -> T {
thread_local!(static INTERNER: RefCell<Interner> = {
RefCell::new(Interner::fresh())
});
INTERNER.with(|interner| f(&mut *interner.borrow_mut()))
}
#[derive(Clone, Hash, PartialOrd, Eq, Ord)]
pub struct InternedString {
string: &'static str,
}
impl<U: ?Sized> ::std::convert::AsRef<U> for InternedString
where
str: ::std::convert::AsRef<U>,
{
fn as_ref(&self) -> &U {
self.string.as_ref()
}
}
impl<T: ::std::ops::Deref<Target = str>> ::std::cmp::PartialEq<T> for InternedString {
fn eq(&self, other: &T) -> bool {
self.string == other.deref()
}
}
impl ::std::cmp::PartialEq<InternedString> for str {
fn eq(&self, other: &InternedString) -> bool {
self == other.string
}
}
impl<'a> ::std::cmp::PartialEq<InternedString> for &'a str {
fn eq(&self, other: &InternedString) -> bool {
*self == other.string
}
}
impl ::std::cmp::PartialEq<InternedString> for String {
fn eq(&self, other: &InternedString) -> bool {
self == other.string
}
}
impl<'a> ::std::cmp::PartialEq<InternedString> for &'a String {
fn eq(&self, other: &InternedString) -> bool {
*self == other.string
}
}
impl ::std::ops::Deref for InternedString {
type Target = str;
fn deref(&self) -> &str {
self.string
}
}
impl fmt::Debug for InternedString {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(self.string, f)
}
}
impl fmt::Display for InternedString {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(self.string, f)
}
}
impl<'de> Deserialize<'de> for InternedString {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Symbol::deserialize(deserializer).map(Symbol::as_str)
}
}
impl Serialize for InternedString {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(self.string)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn interner_tests() {
let mut i: Interner = Interner::new();
assert_eq!(i.intern("dog"), Symbol(0));
assert_eq!(i.intern("dog"), Symbol(0));
assert_eq!(i.intern("cat"), Symbol(1));
assert_eq!(i.intern("cat"), Symbol(1));
assert_eq!(i.intern("dog"), Symbol(0));
assert_eq!(i.gensym("zebra"), Symbol(4294967295));
assert_eq!(i.gensym("zebra"), Symbol(4294967294));
assert_eq!(i.gensym("dog"), Symbol(4294967293));
}
}