rust/src/libsyntax_pos/symbol.rs

425 lines
12 KiB
Rust
Raw Normal View History

// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! An "interner" is a data structure that associates values with usize tags and
//! allows bidirectional lookup; i.e. given a value, one can easily find the
//! type, and vice versa.
2017-03-17 04:04:41 +00:00
use hygiene::SyntaxContext;
use serialize::{Decodable, Decoder, Encodable, Encoder};
use std::cell::RefCell;
use std::collections::HashMap;
use std::fmt;
2017-03-17 04:04:41 +00:00
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct Ident {
pub name: Symbol,
pub ctxt: SyntaxContext,
}
impl Ident {
pub const fn with_empty_ctxt(name: Symbol) -> Ident {
Ident { name: name, ctxt: SyntaxContext::empty() }
}
/// Maps a string to an identifier with an empty syntax context.
pub fn from_str(string: &str) -> Ident {
Ident::with_empty_ctxt(Symbol::intern(string))
}
2017-03-22 08:39:51 +00:00
pub fn modern(self) -> Ident {
Ident { name: self.name, ctxt: self.ctxt.modern() }
2017-03-17 04:04:41 +00:00
}
}
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 Encodable for Ident {
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
2017-03-27 00:46:00 +00:00
if self.ctxt.modern() == SyntaxContext::empty() {
s.emit_str(&self.name.as_str())
} else { // FIXME(jseyfried) intercrate hygiene
let mut string = "#".to_owned();
string.push_str(&self.name.as_str());
s.emit_str(&string)
}
2017-03-17 04:04:41 +00:00
}
}
impl Decodable for Ident {
fn decode<D: Decoder>(d: &mut D) -> Result<Ident, D::Error> {
2017-03-27 00:46:00 +00:00
let string = d.read_str()?;
Ok(if !string.starts_with('#') {
Ident::from_str(&string)
} else { // FIXME(jseyfried) intercrate hygiene
Ident::with_empty_ctxt(Symbol::gensym(&string[1..]))
})
2017-03-17 04:04:41 +00:00
}
}
/// A symbol is an interned or gensymed string.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Symbol(u32);
2016-11-19 05:55:28 +00:00
// The interner in thread-local, so `Symbol` shouldn't move between threads.
impl !Send for Symbol { }
impl !Sync for Symbol { }
2016-11-19 05:55:28 +00:00
impl Symbol {
/// Maps a string to its interned representation.
pub fn intern(string: &str) -> Self {
with_interner(|interner| interner.intern(string))
}
2017-03-26 02:11:30 +00:00
pub fn interned(self) -> Self {
with_interner(|interner| interner.interned(self))
}
/// gensym's a new usize, using the current interner.
pub fn gensym(string: &str) -> Self {
with_interner(|interner| interner.gensym(string))
}
2017-03-26 02:11:30 +00:00
pub fn gensymed(self) -> Self {
with_interner(|interner| interner.gensymed(self))
}
pub fn as_str(self) -> InternedString {
2016-11-19 05:55:28 +00:00
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 Encodable for Symbol {
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
s.emit_str(&self.as_str())
}
}
impl Decodable for Symbol {
fn decode<D: Decoder>(d: &mut D) -> Result<Symbol, D::Error> {
Ok(Symbol::intern(&d.read_str()?))
}
}
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 {
2016-11-19 05:55:28 +00:00
names: HashMap<Box<str>, Symbol>,
strings: Vec<Box<str>>,
2017-03-26 02:11:30 +00:00
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);
2016-11-19 05:55:28 +00:00
let string = string.to_string().into_boxed_str();
self.strings.push(string.clone());
self.names.insert(string, name);
name
}
2017-03-26 02:11:30 +00:00
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 {
2017-03-26 02:11:30 +00:00
let symbol = self.intern(string);
self.gensymed(symbol)
}
2017-03-26 02:11:30 +00:00
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]),
}
}
}
// In this macro, there is the requirement that the name (the number) must be monotonically
// increasing by one in the special identifiers, starting at 0; the same holds for the keywords,
// except starting from the next number instead of zero.
macro_rules! declare_keywords {(
$( ($index: expr, $konst: ident, $string: expr) )*
) => {
pub mod keywords {
2017-03-17 04:04:41 +00:00
use super::{Symbol, Ident};
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct Keyword {
2017-03-17 04:04:41 +00:00
ident: Ident,
}
impl Keyword {
2017-03-17 04:04:41 +00:00
#[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 {
2017-03-17 04:04:41 +00:00
ident: Ident::with_empty_ctxt(super::Symbol($index))
};
)*
}
impl Interner {
fn fresh() -> Self {
Interner::prefill(&[$($string,)*])
}
}
}}
// NB: leaving holes in the ident table is bad! a different ident will get
// interned with the id from the hole, but it will be between the min and max
// of the reserved words, and thus tagged as "reserved".
// After modifying this list adjust `is_strict_keyword`/`is_reserved_keyword`,
// this should be rarely necessary though if the keywords are kept in alphabetic order.
declare_keywords! {
// Invalid identifier
(0, Invalid, "")
// Strict keywords used in the language.
(1, As, "as")
(2, Box, "box")
(3, Break, "break")
(4, Const, "const")
(5, Continue, "continue")
(6, Crate, "crate")
2017-06-25 17:34:49 +00:00
(7, DollarCrate, "$crate")
(8, Else, "else")
(9, Enum, "enum")
(10, Extern, "extern")
(11, False, "false")
(12, Fn, "fn")
(13, For, "for")
(14, If, "if")
(15, Impl, "impl")
(16, In, "in")
(17, Let, "let")
(18, Loop, "loop")
(19, Match, "match")
(20, Mod, "mod")
(21, Move, "move")
(22, Mut, "mut")
(23, Pub, "pub")
(24, Ref, "ref")
(25, Return, "return")
(26, SelfValue, "self")
(27, SelfType, "Self")
(28, Static, "static")
(29, Struct, "struct")
(30, Super, "super")
(31, Trait, "trait")
(32, True, "true")
(33, Type, "type")
(34, Unsafe, "unsafe")
(35, Use, "use")
(36, Where, "where")
(37, While, "while")
// Keywords reserved for future use.
2017-06-25 17:34:49 +00:00
(38, Abstract, "abstract")
(39, Alignof, "alignof")
(40, Become, "become")
(41, Do, "do")
(42, Final, "final")
(43, Macro, "macro")
(44, Offsetof, "offsetof")
(45, Override, "override")
(46, Priv, "priv")
(47, Proc, "proc")
(48, Pure, "pure")
(49, Sizeof, "sizeof")
(50, Typeof, "typeof")
(51, Unsized, "unsized")
(52, Virtual, "virtual")
(53, Yield, "yield")
// Weak keywords, have special meaning only in specific contexts.
2017-06-25 17:34:49 +00:00
(54, Default, "default")
(55, StaticLifetime, "'static")
(56, Union, "union")
(57, Catch, "catch")
// A virtual keyword that resolves to the crate root when used in a lexical scope.
2017-06-25 17:34:49 +00:00
(58, CrateRoot, "{{root}}")
}
// If an interner exists in TLS, return it. Otherwise, prepare a fresh one.
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()))
}
/// 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.
///
/// FIXME(pcwalton): You must be careful about what you do in the destructors
/// of objects stored in TLS, because they may run after the interner is
/// destroyed. In particular, they must not access string contents. This can
/// be fixed in the future by just leaking all strings until thread death
/// somehow.
#[derive(Clone, Hash, PartialOrd, Eq, Ord)]
pub struct InternedString {
2016-11-19 05:55:28 +00:00
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
}
}
2016-11-19 05:55:28 +00:00
impl !Send for InternedString { }
impl ::std::ops::Deref for InternedString {
type Target = str;
2016-11-19 05:55:28 +00:00
fn deref(&self) -> &str { self.string }
}
impl fmt::Debug for InternedString {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2016-11-19 05:55:28 +00:00
fmt::Debug::fmt(self.string, f)
}
}
impl fmt::Display for InternedString {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2016-11-19 05:55:28 +00:00
fmt::Display::fmt(self.string, f)
}
}
impl Decodable for InternedString {
fn decode<D: Decoder>(d: &mut D) -> Result<InternedString, D::Error> {
Ok(Symbol::intern(&d.read_str()?).as_str())
}
}
impl Encodable for InternedString {
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
2016-11-19 05:55:28 +00:00
s.emit_str(self.string)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn interner_tests() {
let mut i: Interner = Interner::new();
// first one is zero:
2017-01-08 21:34:52 +00:00
assert_eq!(i.intern("dog"), Symbol(0));
// re-use gets the same entry:
2017-01-08 21:34:52 +00:00
assert_eq!(i.intern ("dog"), Symbol(0));
// different string gets a different #:
2017-01-08 21:34:52 +00:00
assert_eq!(i.intern("cat"), Symbol(1));
assert_eq!(i.intern("cat"), Symbol(1));
// dog is still at zero
2017-01-08 21:34:52 +00:00
assert_eq!(i.intern("dog"), Symbol(0));
2017-03-26 02:11:30 +00:00
assert_eq!(i.gensym("zebra"), Symbol(4294967295));
// gensym of same string gets new number :
2017-03-26 02:11:30 +00:00
assert_eq!(i.gensym("zebra"), Symbol(4294967294));
// gensym of *existing* string gets new number:
2017-03-26 02:11:30 +00:00
assert_eq!(i.gensym("dog"), Symbol(4294967293));
}
}