use std::cell::RefCell;
use std::collections::HashMap;
use std::fmt;
-use std::rc::Rc;
/// A symbol is an interned or gensymed string.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Symbol(u32);
+// The interner in thread-local, so `Symbol` shouldn't move between threads.
+impl !Send for Symbol { }
+
impl Symbol {
/// Maps a string to its interned representation.
pub fn intern(string: &str) -> Self {
}
pub fn as_str(self) -> InternedString {
- with_interner(|interner| InternedString { string: interner.get(self) })
+ with_interner(|interner| unsafe {
+ InternedString {
+ string: ::std::mem::transmute::<&str, &str>(interner.get(self))
+ }
+ })
}
pub fn as_u32(self) -> u32 {
#[derive(Default)]
pub struct Interner {
- names: HashMap<Rc<str>, Symbol>,
- strings: Vec<Rc<str>>,
+ names: HashMap<Box<str>, Symbol>,
+ strings: Vec<Box<str>>,
}
impl Interner {
}
let name = Symbol(self.strings.len() as u32);
- let string = Rc::__from_str(string);
+ let string = string.to_string().into_boxed_str();
self.strings.push(string.clone());
self.names.insert(string, name);
name
fn gensym(&mut self, string: &str) -> Symbol {
let gensym = Symbol(self.strings.len() as u32);
// leave out of `names` to avoid colliding
- self.strings.push(Rc::__from_str(string));
+ self.strings.push(string.to_string().into_boxed_str());
gensym
}
- pub fn get(&self, name: Symbol) -> Rc<str> {
- self.strings[name.0 as usize].clone()
+ pub fn get(&self, name: Symbol) -> &str {
+ &self.strings[name.0 as usize]
}
}
$(
#[allow(non_upper_case_globals)]
pub const $konst: Keyword = Keyword {
- ident: ast::Ident::with_empty_ctxt(ast::Name($index))
+ ident: ast::Ident::with_empty_ctxt(super::Symbol($index))
};
)*
}
(53, Default, "default")
(54, StaticLifetime, "'static")
(55, Union, "union")
+
+ // A virtual keyword that resolves to the crate root when used in a lexical scope.
+ (56, CrateRoot, "{{root}}")
}
// If an interner exists in TLS, return it. Otherwise, prepare a fresh one.
INTERNER.with(|interner| f(&mut *interner.borrow_mut()))
}
-/// Reset the ident interner to its initial state.
-pub fn reset_interner() {
- with_interner(|interner| *interner = Interner::fresh());
-}
-
/// Represents a string stored in the thread-local interner. Because the
/// interner lives for the life of the thread, this can be safely treated as an
/// immortal string, as long as it never crosses between threads.
/// somehow.
#[derive(Clone, PartialEq, Hash, PartialOrd, Eq, Ord)]
pub struct InternedString {
- string: Rc<str>,
+ string: &'static str,
}
-impl InternedString {
- pub fn new(string: &'static str) -> InternedString {
- InternedString {
- string: Rc::__from_str(string),
- }
- }
-}
+impl !Send for InternedString { }
impl ::std::ops::Deref for InternedString {
type Target = str;
- fn deref(&self) -> &str { &self.string }
+ 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)
+ 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<'a> PartialEq<&'a str> for InternedString {
- fn eq(&self, other: & &'a str) -> bool {
- PartialEq::eq(&self.string[..], *other)
- }
-}
-
-impl<'a> PartialEq<InternedString> for &'a str {
- fn eq(&self, other: &InternedString) -> bool {
- PartialEq::eq(*self, &other.string[..])
- }
-}
-
-impl PartialEq<str> for InternedString {
- fn eq(&self, other: &str) -> bool {
- PartialEq::eq(&self.string[..], other)
- }
-}
-
-impl PartialEq<InternedString> for str {
- fn eq(&self, other: &InternedString) -> bool {
- PartialEq::eq(self, &other.string[..])
+ fmt::Display::fmt(self.string, f)
}
}
impl Encodable for InternedString {
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
- s.emit_str(&self.string)
+ s.emit_str(self.string)
}
}
#[cfg(test)]
mod tests {
use super::*;
- use ast::Name;
#[test]
fn interner_tests() {
let mut i: Interner = Interner::new();
// first one is zero:
- assert_eq!(i.intern("dog"), Name(0));
+ assert_eq!(i.intern("dog"), Symbol(0));
// re-use gets the same entry:
- assert_eq!(i.intern ("dog"), Name(0));
+ assert_eq!(i.intern ("dog"), Symbol(0));
// different string gets a different #:
- assert_eq!(i.intern("cat"), Name(1));
- assert_eq!(i.intern("cat"), Name(1));
+ assert_eq!(i.intern("cat"), Symbol(1));
+ assert_eq!(i.intern("cat"), Symbol(1));
// dog is still at zero
- assert_eq!(i.intern("dog"), Name(0));
+ assert_eq!(i.intern("dog"), Symbol(0));
// gensym gets 3
- assert_eq!(i.gensym("zebra"), Name(2));
+ assert_eq!(i.gensym("zebra"), Symbol(2));
// gensym of same string gets new number :
- assert_eq!(i.gensym("zebra"), Name(3));
+ assert_eq!(i.gensym("zebra"), Symbol(3));
// gensym of *existing* string gets new number:
- assert_eq!(i.gensym("dog"), Name(4));
+ assert_eq!(i.gensym("dog"), Symbol(4));
}
}