]> git.proxmox.com Git - rustc.git/blobdiff - src/libsyntax/parse/token.rs
New upstream version 1.23.0+dfsg1
[rustc.git] / src / libsyntax / parse / token.rs
index 832fec40199b8ae9be51f7ed59abf0368c50b08a..f83343bf9afa51fe1785c2a0163699e3cc157467 100644 (file)
 pub use self::BinOpToken::*;
 pub use self::Nonterminal::*;
 pub use self::DelimToken::*;
-pub use self::IdentStyle::*;
 pub use self::Lit::*;
 pub use self::Token::*;
 
-use ast;
-use ext::mtwt;
+use ast::{self};
+use parse::ParseSess;
+use print::pprust;
 use ptr::P;
-use util::interner::{RcStr, StrInterner};
-use util::interner;
-
 use serialize::{Decodable, Decoder, Encodable, Encoder};
-use std::fmt;
-use std::ops::Deref;
+use symbol::keywords;
+use syntax::parse::parse_stream_from_source_str;
+use syntax_pos::{self, Span};
+use tokenstream::{TokenStream, TokenTree};
+use tokenstream;
+
+use std::cell::Cell;
+use std::{cmp, fmt};
 use std::rc::Rc;
 
-#[allow(non_camel_case_types)]
 #[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Eq, Hash, Debug, Copy)]
 pub enum BinOpToken {
     Plus,
@@ -50,27 +52,17 @@ pub enum DelimToken {
     Bracket,
     /// A curly brace: `{` or `}`
     Brace,
+    /// An empty delimiter
+    NoDelim,
 }
 
-#[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Eq, Hash, Debug, Copy)]
-pub enum IdentStyle {
-    /// `::` follows the identifier with no whitespace in-between.
-    ModName,
-    Plain,
-}
-
-#[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Eq, Hash, Debug, Copy)]
-pub enum SpecialMacroVar {
-    /// `$crate` will be filled in with the name of the crate a macro was
-    /// imported from, if any.
-    CrateMacroVar,
-}
+impl DelimToken {
+    pub fn len(self) -> usize {
+        if self == NoDelim { 0 } else { 1 }
+    }
 
-impl SpecialMacroVar {
-    pub fn as_str(self) -> &'static str {
-        match self {
-            SpecialMacroVar::CrateMacroVar => "crate",
-        }
+    pub fn is_empty(self) -> bool {
+        self == NoDelim
     }
 }
 
@@ -82,8 +74,8 @@ pub enum Lit {
     Float(ast::Name),
     Str_(ast::Name),
     StrRaw(ast::Name, usize), /* raw str delimited by n hash symbols */
-    Binary(ast::Name),
-    BinaryRaw(ast::Name, usize), /* raw binary str delimited by n hash symbols */
+    ByteStr(ast::Name),
+    ByteStrRaw(ast::Name, usize), /* raw byte str delimited by n hash symbols */
 }
 
 impl Lit {
@@ -93,13 +85,51 @@ impl Lit {
             Char(_) => "char",
             Integer(_) => "integer",
             Float(_) => "float",
-            Str_(_) | StrRaw(..) => "str",
-            Binary(_) | BinaryRaw(..) => "binary str"
+            Str_(_) | StrRaw(..) => "string",
+            ByteStr(_) | ByteStrRaw(..) => "byte string"
         }
     }
 }
 
-#[allow(non_camel_case_types)]
+fn ident_can_begin_expr(ident: ast::Ident) -> bool {
+    let ident_token: Token = Ident(ident);
+
+    !ident_token.is_reserved_ident() ||
+    ident_token.is_path_segment_keyword() ||
+    [
+        keywords::Do.name(),
+        keywords::Box.name(),
+        keywords::Break.name(),
+        keywords::Continue.name(),
+        keywords::False.name(),
+        keywords::For.name(),
+        keywords::If.name(),
+        keywords::Loop.name(),
+        keywords::Match.name(),
+        keywords::Move.name(),
+        keywords::Return.name(),
+        keywords::True.name(),
+        keywords::Unsafe.name(),
+        keywords::While.name(),
+        keywords::Yield.name(),
+    ].contains(&ident.name)
+}
+
+fn ident_can_begin_type(ident: ast::Ident) -> bool {
+    let ident_token: Token = Ident(ident);
+
+    !ident_token.is_reserved_ident() ||
+    ident_token.is_path_segment_keyword() ||
+    [
+        keywords::For.name(),
+        keywords::Impl.name(),
+        keywords::Fn.name(),
+        keywords::Unsafe.name(),
+        keywords::Extern.name(),
+        keywords::Typeof.name(),
+    ].contains(&ident.name)
+}
+
 #[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Eq, Hash, Debug)]
 pub enum Token {
     /* Expression-operator symbols. */
@@ -122,6 +152,8 @@ pub enum Token {
     Dot,
     DotDot,
     DotDotDot,
+    DotDotEq,
+    DotEq, // HACK(durka42) never produced by the parser, only used for libproc_macro
     Comma,
     Semi,
     Colon,
@@ -141,23 +173,16 @@ pub enum Token {
     Literal(Lit, Option<ast::Name>),
 
     /* Name components */
-    Ident(ast::Ident, IdentStyle),
+    Ident(ast::Ident),
     Underscore,
     Lifetime(ast::Ident),
 
-    /* For interpolation */
-    Interpolated(Nonterminal),
+    // The `LazyTokenStream` is a pure function of the `Nonterminal`,
+    // and so the `LazyTokenStream` can be ignored by Eq, Hash, etc.
+    Interpolated(Rc<(Nonterminal, LazyTokenStream)>),
     // Can be expanded into several tokens.
     /// Doc comment
     DocComment(ast::Name),
-    // In left-hand-sides of MBE macros:
-    /// Parse a nonterminal (name to bind, name of NT, styles of their idents)
-    MatchNt(ast::Ident, ast::Ident, IdentStyle, IdentStyle),
-    // In right-hand-sides of MBE macros:
-    /// A syntactic variable that will be filled in by macro expansion.
-    SubstNt(ast::Ident, IdentStyle),
-    /// A macro variable with special meaning.
-    SpecialVarNt(SpecialMacroVar),
 
     // Junk. These carry no data because we don't really care about the data
     // they *would* carry, and don't really want to allocate a new ident for
@@ -173,6 +198,10 @@ pub enum Token {
 }
 
 impl Token {
+    pub fn interpolated(nt: Nonterminal) -> Token {
+        Token::Interpolated(Rc::new((nt, LazyTokenStream::new())))
+    }
+
     /// Returns `true` if the token starts with '>'.
     pub fn is_like_gt(&self) -> bool {
         match *self {
@@ -184,60 +213,105 @@ impl Token {
     /// Returns `true` if the token can appear at the start of an expression.
     pub fn can_begin_expr(&self) -> bool {
         match *self {
-            OpenDelim(_)                => true,
-            Ident(_, _)                 => true,
-            Underscore                  => true,
-            Tilde                       => true,
-            Literal(_, _)               => true,
-            Not                         => true,
-            BinOp(Minus)                => true,
-            BinOp(Star)                 => true,
-            BinOp(And)                  => true,
-            BinOp(Or)                   => true, // in lambda syntax
-            OrOr                        => true, // in lambda syntax
-            AndAnd                      => true, // double borrow
-            DotDot                      => true, // range notation
-            ModSep                      => true,
-            Interpolated(NtExpr(..))    => true,
-            Interpolated(NtIdent(..))   => true,
-            Interpolated(NtBlock(..))   => true,
-            Interpolated(NtPath(..))    => true,
-            _                           => false,
+            Ident(ident)                => ident_can_begin_expr(ident), // value name or keyword
+            OpenDelim(..)                     | // tuple, array or block
+            Literal(..)                       | // literal
+            Not                               | // operator not
+            BinOp(Minus)                      | // unary minus
+            BinOp(Star)                       | // dereference
+            BinOp(Or) | OrOr                  | // closure
+            BinOp(And)                        | // reference
+            AndAnd                            | // double reference
+            // DotDotDot is no longer supported, but we need some way to display the error
+            DotDot | DotDotDot | DotDotEq     | // range notation
+            Lt | BinOp(Shl)                   | // associated path
+            ModSep                            | // global path
+            Pound                             => true, // expression attributes
+            Interpolated(ref nt) => match nt.0 {
+                NtIdent(..) | NtExpr(..) | NtBlock(..) | NtPath(..) => true,
+                _ => false,
+            },
+            _ => false,
+        }
+    }
+
+    /// Returns `true` if the token can appear at the start of a type.
+    pub fn can_begin_type(&self) -> bool {
+        match *self {
+            Ident(ident)                => ident_can_begin_type(ident), // type name or keyword
+            OpenDelim(Paren)            | // tuple
+            OpenDelim(Bracket)          | // array
+            Underscore                  | // placeholder
+            Not                         | // never
+            BinOp(Star)                 | // raw pointer
+            BinOp(And)                  | // reference
+            AndAnd                      | // double reference
+            Question                    | // maybe bound in trait object
+            Lifetime(..)                | // lifetime bound in trait object
+            Lt | BinOp(Shl)             | // associated path
+            ModSep                      => true, // global path
+            Interpolated(ref nt) => match nt.0 {
+                NtIdent(..) | NtTy(..) | NtPath(..) => true,
+                _ => false,
+            },
+            _ => false,
         }
     }
 
+    /// Returns `true` if the token can appear at the start of a generic bound.
+    pub fn can_begin_bound(&self) -> bool {
+        self.is_path_start() || self.is_lifetime() || self.is_keyword(keywords::For) ||
+        self == &Question || self == &OpenDelim(Paren)
+    }
+
     /// Returns `true` if the token is any literal
     pub fn is_lit(&self) -> bool {
         match *self {
-            Literal(_, _) => true,
-            _          => false,
+            Literal(..) => true,
+            _           => false,
+        }
+    }
+
+    pub fn ident(&self) -> Option<ast::Ident> {
+        match *self {
+            Ident(ident) => Some(ident),
+            Interpolated(ref nt) => match nt.0 {
+                NtIdent(ident) => Some(ident.node),
+                _ => None,
+            },
+            _ => None,
         }
     }
 
     /// Returns `true` if the token is an identifier.
     pub fn is_ident(&self) -> bool {
+        self.ident().is_some()
+    }
+
+    /// Returns `true` if the token is a documentation comment.
+    pub fn is_doc_comment(&self) -> bool {
         match *self {
-            Ident(_, _) => true,
-            _           => false,
+            DocComment(..)   => true,
+            _                => false,
         }
     }
 
-    /// Returns `true` if the token is an interpolated path.
-    pub fn is_path(&self) -> bool {
+    /// Returns `true` if the token is interpolated.
+    pub fn is_interpolated(&self) -> bool {
         match *self {
-            Interpolated(NtPath(..))    => true,
-            _                           => false,
+            Interpolated(..) => true,
+            _                => false,
         }
     }
 
-    /// Returns `true` if the token is a path that is not followed by a `::`
-    /// token.
-    #[allow(non_upper_case_globals)]
-    pub fn is_plain_ident(&self) -> bool {
-        match *self {
-            Ident(_, Plain) => true,
-            _               => false,
+    /// Returns `true` if the token is an interpolated path.
+    pub fn is_path(&self) -> bool {
+        if let Interpolated(ref nt) = *self {
+            if let NtPath(..) = nt.0 {
+                return true;
+            }
         }
+        false
     }
 
     /// Returns `true` if the token is a lifetime.
@@ -254,116 +328,177 @@ impl Token {
         self.is_keyword(keywords::Const)
     }
 
-    /// Maps a token to its corresponding binary operator.
-    pub fn to_binop(&self) -> Option<ast::BinOp_> {
-        match *self {
-            BinOp(Star)     => Some(ast::BiMul),
-            BinOp(Slash)    => Some(ast::BiDiv),
-            BinOp(Percent)  => Some(ast::BiRem),
-            BinOp(Plus)     => Some(ast::BiAdd),
-            BinOp(Minus)    => Some(ast::BiSub),
-            BinOp(Shl)      => Some(ast::BiShl),
-            BinOp(Shr)      => Some(ast::BiShr),
-            BinOp(And)      => Some(ast::BiBitAnd),
-            BinOp(Caret)    => Some(ast::BiBitXor),
-            BinOp(Or)       => Some(ast::BiBitOr),
-            Lt              => Some(ast::BiLt),
-            Le              => Some(ast::BiLe),
-            Ge              => Some(ast::BiGe),
-            Gt              => Some(ast::BiGt),
-            EqEq            => Some(ast::BiEq),
-            Ne              => Some(ast::BiNe),
-            AndAnd          => Some(ast::BiAnd),
-            OrOr            => Some(ast::BiOr),
-            _               => None,
-        }
+    pub fn is_qpath_start(&self) -> bool {
+        self == &Lt || self == &BinOp(Shl)
+    }
+
+    pub fn is_path_start(&self) -> bool {
+        self == &ModSep || self.is_qpath_start() || self.is_path() ||
+        self.is_path_segment_keyword() || self.is_ident() && !self.is_reserved_ident()
     }
 
     /// Returns `true` if the token is a given keyword, `kw`.
-    #[allow(non_upper_case_globals)]
     pub fn is_keyword(&self, kw: keywords::Keyword) -> bool {
-        match *self {
-            Ident(sid, Plain) => kw.to_name() == sid.name,
-            _                      => false,
-        }
+        self.ident().map(|ident| ident.name == kw.name()).unwrap_or(false)
     }
 
-    pub fn is_keyword_allow_following_colon(&self, kw: keywords::Keyword) -> bool {
-        match *self {
-            Ident(sid, _) => { kw.to_name() == sid.name }
-            _ => { false }
+    pub fn is_path_segment_keyword(&self) -> bool {
+        match self.ident() {
+            Some(id) => id.name == keywords::Super.name() ||
+                        id.name == keywords::SelfValue.name() ||
+                        id.name == keywords::SelfType.name() ||
+                        id.name == keywords::DollarCrate.name(),
+            None => false,
         }
     }
 
-    /// Returns `true` if the token is either a special identifier, or a strict
-    /// or reserved keyword.
-    #[allow(non_upper_case_globals)]
-    pub fn is_any_keyword(&self) -> bool {
-        match *self {
-            Ident(sid, Plain) => {
-                let n = sid.name;
-
-                   n == SELF_KEYWORD_NAME
-                || n == STATIC_KEYWORD_NAME
-                || n == SUPER_KEYWORD_NAME
-                || n == SELF_TYPE_KEYWORD_NAME
-                || STRICT_KEYWORD_START <= n
-                && n <= RESERVED_KEYWORD_FINAL
-            },
-            _ => false
+    // Returns true for reserved identifiers used internally for elided lifetimes,
+    // unnamed method parameters, crate root module, error recovery etc.
+    pub fn is_special_ident(&self) -> bool {
+        match self.ident() {
+            Some(id) => id.name <= keywords::DollarCrate.name(),
+            _ => false,
         }
     }
 
-    /// Returns `true` if the token may not appear as an identifier.
-    #[allow(non_upper_case_globals)]
-    pub fn is_strict_keyword(&self) -> bool {
-        match *self {
-            Ident(sid, Plain) => {
-                let n = sid.name;
-
-                   n == SELF_KEYWORD_NAME
-                || n == STATIC_KEYWORD_NAME
-                || n == SUPER_KEYWORD_NAME
-                || n == SELF_TYPE_KEYWORD_NAME
-                || STRICT_KEYWORD_START <= n
-                && n <= STRICT_KEYWORD_FINAL
-            },
-            Ident(sid, ModName) => {
-                let n = sid.name;
-
-                   n != SELF_KEYWORD_NAME
-                && n != SUPER_KEYWORD_NAME
-                && STRICT_KEYWORD_START <= n
-                && n <= STRICT_KEYWORD_FINAL
-            }
+    /// Returns `true` if the token is a keyword used in the language.
+    pub fn is_used_keyword(&self) -> bool {
+        match self.ident() {
+            Some(id) => id.name >= keywords::As.name() && id.name <= keywords::While.name(),
             _ => false,
         }
     }
 
-    /// Returns `true` if the token is a keyword that has been reserved for
-    /// possible future use.
-    #[allow(non_upper_case_globals)]
-    pub fn is_reserved_keyword(&self) -> bool {
-        match *self {
-            Ident(sid, Plain) => {
-                let n = sid.name;
+    /// Returns `true` if the token is a keyword reserved for possible future use.
+    pub fn is_unused_keyword(&self) -> bool {
+        match self.ident() {
+            Some(id) => id.name >= keywords::Abstract.name() && id.name <= keywords::Yield.name(),
+            _ => false,
+        }
+    }
 
-                   RESERVED_KEYWORD_START <= n
-                && n <= RESERVED_KEYWORD_FINAL
+    pub fn glue(self, joint: Token) -> Option<Token> {
+        Some(match self {
+            Eq => match joint {
+                Eq => EqEq,
+                Gt => FatArrow,
+                _ => return None,
             },
-            _ => false,
+            Lt => match joint {
+                Eq => Le,
+                Lt => BinOp(Shl),
+                Le => BinOpEq(Shl),
+                BinOp(Minus) => LArrow,
+                _ => return None,
+            },
+            Gt => match joint {
+                Eq => Ge,
+                Gt => BinOp(Shr),
+                Ge => BinOpEq(Shr),
+                _ => return None,
+            },
+            Not => match joint {
+                Eq => Ne,
+                _ => return None,
+            },
+            BinOp(op) => match joint {
+                Eq => BinOpEq(op),
+                BinOp(And) if op == And => AndAnd,
+                BinOp(Or) if op == Or => OrOr,
+                Gt if op == Minus => RArrow,
+                _ => return None,
+            },
+            Dot => match joint {
+                Dot => DotDot,
+                DotDot => DotDotDot,
+                DotEq => DotDotEq,
+                _ => return None,
+            },
+            DotDot => match joint {
+                Dot => DotDotDot,
+                Eq => DotDotEq,
+                _ => return None,
+            },
+            Colon => match joint {
+                Colon => ModSep,
+                _ => return None,
+            },
+
+            Le | EqEq | Ne | Ge | AndAnd | OrOr | Tilde | BinOpEq(..) | At | DotDotDot | DotEq |
+            DotDotEq | Comma | Semi | ModSep | RArrow | LArrow | FatArrow | Pound | Dollar |
+            Question | OpenDelim(..) | CloseDelim(..) | Underscore => return None,
+
+            Literal(..) | Ident(..) | Lifetime(..) | Interpolated(..) | DocComment(..) |
+            Whitespace | Comment | Shebang(..) | Eof => return None,
+        })
+    }
+
+    /// Returns tokens that are likely to be typed accidentally instead of the current token.
+    /// Enables better error recovery when the wrong token is found.
+    pub fn similar_tokens(&self) -> Option<Vec<Token>> {
+        match *self {
+            Comma => Some(vec![Dot, Lt]),
+            Semi => Some(vec![Colon]),
+            _ => None
         }
     }
 
-    /// Hygienic identifier equality comparison.
-    ///
-    /// See `styntax::ext::mtwt`.
-    pub fn mtwt_eq(&self, other : &Token) -> bool {
-        match (self, other) {
-            (&Ident(id1,_), &Ident(id2,_)) | (&Lifetime(id1), &Lifetime(id2)) =>
-                mtwt::resolve(id1) == mtwt::resolve(id2),
-            _ => *self == *other
+    /// Returns `true` if the token is either a special identifier or a keyword.
+    pub fn is_reserved_ident(&self) -> bool {
+        self.is_special_ident() || self.is_used_keyword() || self.is_unused_keyword()
+    }
+
+    pub fn interpolated_to_tokenstream(&self, sess: &ParseSess, span: Span)
+        -> TokenStream
+    {
+        let nt = match *self {
+            Token::Interpolated(ref nt) => nt,
+            _ => panic!("only works on interpolated tokens"),
+        };
+
+        // An `Interpolated` token means that we have a `Nonterminal`
+        // which is often a parsed AST item. At this point we now need
+        // to convert the parsed AST to an actual token stream, e.g.
+        // un-parse it basically.
+        //
+        // Unfortunately there's not really a great way to do that in a
+        // guaranteed lossless fashion right now. The fallback here is
+        // to just stringify the AST node and reparse it, but this loses
+        // all span information.
+        //
+        // As a result, some AST nodes are annotated with the token
+        // stream they came from. Attempt to extract these lossless
+        // token streams before we fall back to the stringification.
+        let mut tokens = None;
+
+        match nt.0 {
+            Nonterminal::NtItem(ref item) => {
+                tokens = prepend_attrs(sess, &item.attrs, item.tokens.as_ref(), span);
+            }
+            Nonterminal::NtTraitItem(ref item) => {
+                tokens = prepend_attrs(sess, &item.attrs, item.tokens.as_ref(), span);
+            }
+            Nonterminal::NtImplItem(ref item) => {
+                tokens = prepend_attrs(sess, &item.attrs, item.tokens.as_ref(), span);
+            }
+            Nonterminal::NtIdent(ident) => {
+                let token = Token::Ident(ident.node);
+                tokens = Some(TokenTree::Token(ident.span, token).into());
+            }
+            Nonterminal::NtTT(ref tt) => {
+                tokens = Some(tt.clone().into());
+            }
+            _ => {}
         }
+
+        tokens.unwrap_or_else(|| {
+            nt.1.force(|| {
+                // FIXME(jseyfried): Avoid this pretty-print + reparse hack
+                let name = "<macro expansion>".to_owned();
+                let source = pprust::token_to_string(self);
+                parse_stream_from_source_str(name, source, sess, Some(span))
+            })
+        })
     }
 }
 
@@ -372,21 +507,23 @@ impl Token {
 pub enum Nonterminal {
     NtItem(P<ast::Item>),
     NtBlock(P<ast::Block>),
-    NtStmt(P<ast::Stmt>),
+    NtStmt(ast::Stmt),
     NtPat(P<ast::Pat>),
     NtExpr(P<ast::Expr>),
     NtTy(P<ast::Ty>),
-    NtIdent(Box<ast::Ident>, IdentStyle),
+    NtIdent(ast::SpannedIdent),
     /// Stuff inside brackets for attributes
-    NtMeta(P<ast::MetaItem>),
-    NtPath(Box<ast::Path>),
-    NtTT(P<ast::TokenTree>), // needs P'ed to break a circularity
-    // These is not exposed to macros, but is used by quasiquote.
+    NtMeta(ast::MetaItem),
+    NtPath(ast::Path),
+    NtVis(ast::Visibility),
+    NtTT(TokenTree),
+    // These are not exposed to macros, but are used by quasiquote.
     NtArm(ast::Arm),
-    NtImplItem(P<ast::ImplItem>),
-    NtTraitItem(P<ast::TraitItem>),
+    NtImplItem(ast::ImplItem),
+    NtTraitItem(ast::TraitItem),
     NtGenerics(ast::Generics),
     NtWhereClause(ast::WhereClause),
+    NtArg(ast::Arg),
 }
 
 impl fmt::Debug for Nonterminal {
@@ -407,377 +544,98 @@ impl fmt::Debug for Nonterminal {
             NtTraitItem(..) => f.pad("NtTraitItem(..)"),
             NtGenerics(..) => f.pad("NtGenerics(..)"),
             NtWhereClause(..) => f.pad("NtWhereClause(..)"),
+            NtArg(..) => f.pad("NtArg(..)"),
+            NtVis(..) => f.pad("NtVis(..)"),
         }
     }
 }
 
-
-// Get the first "argument"
-macro_rules! first {
-    ( $first:expr, $( $remainder:expr, )* ) => ( $first )
-}
-
-// Get the last "argument" (has to be done recursively to avoid phoney local ambiguity error)
-macro_rules! last {
-    ( $first:expr, $( $remainder:expr, )+ ) => ( last!( $( $remainder, )+ ) );
-    ( $first:expr, ) => ( $first )
-}
-
-// 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, and with the additional exception that
-// special identifiers are *also* allowed (they are deduplicated in the important place, the
-// interner), an exception which is demonstrated by "static" and "self".
-macro_rules! declare_special_idents_and_keywords {(
-    // So now, in these rules, why is each definition parenthesised?
-    // Answer: otherwise we get a spurious local ambiguity bug on the "}"
-    pub mod special_idents {
-        $( ($si_name:expr, $si_static:ident, $si_str:expr); )*
-    }
-
-    pub mod keywords {
-        'strict:
-        $( ($sk_name:expr, $sk_variant:ident, $sk_str:expr); )*
-        'reserved:
-        $( ($rk_name:expr, $rk_variant:ident, $rk_str:expr); )*
-    }
-) => {
-    const STRICT_KEYWORD_START: ast::Name = first!($( ast::Name($sk_name), )*);
-    const STRICT_KEYWORD_FINAL: ast::Name = last!($( ast::Name($sk_name), )*);
-    const RESERVED_KEYWORD_START: ast::Name = first!($( ast::Name($rk_name), )*);
-    const RESERVED_KEYWORD_FINAL: ast::Name = last!($( ast::Name($rk_name), )*);
-
-    pub mod special_idents {
-        use ast;
-        $(
-            #[allow(non_upper_case_globals)]
-            pub const $si_static: ast::Ident = ast::Ident {
-                name: ast::Name($si_name),
-                ctxt: 0,
-            };
-         )*
-    }
-
-    pub mod special_names {
-        use ast;
-        $(
-            #[allow(non_upper_case_globals)]
-            pub const $si_static: ast::Name =  ast::Name($si_name);
-        )*
-    }
-
-    /// All the valid words that have meaning in the Rust language.
-    ///
-    /// Rust keywords are either 'strict' or 'reserved'.  Strict keywords may not
-    /// appear as identifiers at all. Reserved keywords are not used anywhere in
-    /// the language and may not appear as identifiers.
-    pub mod keywords {
-        pub use self::Keyword::*;
-        use ast;
-
-        #[derive(Copy, Clone, PartialEq, Eq)]
-        pub enum Keyword {
-            $( $sk_variant, )*
-            $( $rk_variant, )*
-        }
-
-        impl Keyword {
-            pub fn to_name(&self) -> ast::Name {
-                match *self {
-                    $( $sk_variant => ast::Name($sk_name), )*
-                    $( $rk_variant => ast::Name($rk_name), )*
-                }
-            }
-        }
+pub fn is_op(tok: &Token) -> bool {
+    match *tok {
+        OpenDelim(..) | CloseDelim(..) | Literal(..) | DocComment(..) |
+        Ident(..) | Underscore | Lifetime(..) | Interpolated(..) |
+        Whitespace | Comment | Shebang(..) | Eof => false,
+        _ => true,
     }
-
-    fn mk_fresh_ident_interner() -> IdentInterner {
-        // The indices here must correspond to the numbers in
-        // special_idents, in Keyword to_name(), and in static
-        // constants below.
-        let mut init_vec = Vec::new();
-        $(init_vec.push($si_str);)*
-        $(init_vec.push($sk_str);)*
-        $(init_vec.push($rk_str);)*
-        interner::StrInterner::prefill(&init_vec[..])
-    }
-}}
-
-// If the special idents get renumbered, remember to modify these two as appropriate
-pub const SELF_KEYWORD_NAME: ast::Name = ast::Name(SELF_KEYWORD_NAME_NUM);
-const STATIC_KEYWORD_NAME: ast::Name = ast::Name(STATIC_KEYWORD_NAME_NUM);
-const SUPER_KEYWORD_NAME: ast::Name = ast::Name(SUPER_KEYWORD_NAME_NUM);
-const SELF_TYPE_KEYWORD_NAME: ast::Name = ast::Name(SELF_TYPE_KEYWORD_NAME_NUM);
-
-pub const SELF_KEYWORD_NAME_NUM: u32 = 1;
-const STATIC_KEYWORD_NAME_NUM: u32 = 2;
-const SUPER_KEYWORD_NAME_NUM: u32 = 3;
-const SELF_TYPE_KEYWORD_NAME_NUM: u32 = 10;
-
-// 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".
-
-declare_special_idents_and_keywords! {
-    pub mod special_idents {
-        // These ones are statics
-        (0,                          invalid,                "");
-        (super::SELF_KEYWORD_NAME_NUM,   self_,              "self");
-        (super::STATIC_KEYWORD_NAME_NUM, statik,             "static");
-        (super::SUPER_KEYWORD_NAME_NUM, super_,              "super");
-        (4,                          static_lifetime,        "'static");
-
-        // for matcher NTs
-        (5,                          tt,                     "tt");
-        (6,                          matchers,               "matchers");
-
-        // outside of libsyntax
-        (7,                          clownshoe_abi,          "__rust_abi");
-        (8,                          opaque,                 "<opaque>");
-        (9,                          unnamed_field,          "<unnamed_field>");
-        (super::SELF_TYPE_KEYWORD_NAME_NUM, type_self,       "Self");
-        (11,                         prelude_import,         "prelude_import");
-    }
-
-    pub mod keywords {
-        // These ones are variants of the Keyword enum
-
-        'strict:
-        (12,                         As,         "as");
-        (13,                         Break,      "break");
-        (14,                         Crate,      "crate");
-        (15,                         Else,       "else");
-        (16,                         Enum,       "enum");
-        (17,                         Extern,     "extern");
-        (18,                         False,      "false");
-        (19,                         Fn,         "fn");
-        (20,                         For,        "for");
-        (21,                         If,         "if");
-        (22,                         Impl,       "impl");
-        (23,                         In,         "in");
-        (24,                         Let,        "let");
-        (25,                         Loop,       "loop");
-        (26,                         Match,      "match");
-        (27,                         Mod,        "mod");
-        (28,                         Move,       "move");
-        (29,                         Mut,        "mut");
-        (30,                         Pub,        "pub");
-        (31,                         Ref,        "ref");
-        (32,                         Return,     "return");
-        // Static and Self are also special idents (prefill de-dupes)
-        (super::STATIC_KEYWORD_NAME_NUM, Static, "static");
-        (super::SELF_KEYWORD_NAME_NUM, SelfValue, "self");
-        (super::SELF_TYPE_KEYWORD_NAME_NUM, SelfType, "Self");
-        (33,                         Struct,     "struct");
-        (super::SUPER_KEYWORD_NAME_NUM, Super,   "super");
-        (34,                         True,       "true");
-        (35,                         Trait,      "trait");
-        (36,                         Type,       "type");
-        (37,                         Unsafe,     "unsafe");
-        (38,                         Use,        "use");
-        (39,                         Virtual,    "virtual");
-        (40,                         While,      "while");
-        (41,                         Continue,   "continue");
-        (42,                         Box,        "box");
-        (43,                         Const,      "const");
-        (44,                         Where,      "where");
-        'reserved:
-        (45,                         Proc,       "proc");
-        (46,                         Alignof,    "alignof");
-        (47,                         Become,     "become");
-        (48,                         Offsetof,   "offsetof");
-        (49,                         Priv,       "priv");
-        (50,                         Pure,       "pure");
-        (51,                         Sizeof,     "sizeof");
-        (52,                         Typeof,     "typeof");
-        (53,                         Unsized,    "unsized");
-        (54,                         Yield,      "yield");
-        (55,                         Do,         "do");
-        (56,                         Abstract,   "abstract");
-        (57,                         Final,      "final");
-        (58,                         Override,   "override");
-        (59,                         Macro,      "macro");
-    }
-}
-
-// looks like we can get rid of this completely...
-pub type IdentInterner = StrInterner;
-
-// if an interner exists in TLS, return it. Otherwise, prepare a
-// fresh one.
-// FIXME(eddyb) #8726 This should probably use a thread-local reference.
-pub fn get_ident_interner() -> Rc<IdentInterner> {
-    thread_local!(static KEY: Rc<::parse::token::IdentInterner> = {
-        Rc::new(mk_fresh_ident_interner())
-    });
-    KEY.with(|k| k.clone())
-}
-
-/// Reset the ident interner to its initial state.
-pub fn reset_ident_interner() {
-    let interner = get_ident_interner();
-    interner.reset(mk_fresh_ident_interner());
-}
-
-/// 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, PartialEq, Hash, PartialOrd, Eq, Ord)]
-pub struct InternedString {
-    string: RcStr,
 }
 
-impl InternedString {
-    #[inline]
-    pub fn new(string: &'static str) -> InternedString {
-        InternedString {
-            string: RcStr::new(string),
-        }
-    }
+pub struct LazyTokenStream(Cell<Option<TokenStream>>);
 
-    #[inline]
-    fn new_from_rc_str(string: RcStr) -> InternedString {
-        InternedString {
-            string: string,
-        }
+impl Clone for LazyTokenStream {
+    fn clone(&self) -> Self {
+        let opt_stream = self.0.take();
+        self.0.set(opt_stream.clone());
+        LazyTokenStream(Cell::new(opt_stream))
     }
 }
 
-impl 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 cmp::Eq for LazyTokenStream {}
+impl PartialEq for LazyTokenStream {
+    fn eq(&self, _other: &LazyTokenStream) -> bool {
+        true
     }
 }
 
-impl fmt::Display for InternedString {
+impl fmt::Debug for LazyTokenStream {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        fmt::Display::fmt(&self.string, f)
+        fmt::Debug::fmt(&self.clone().0.into_inner(), f)
     }
 }
 
-impl<'a> PartialEq<&'a str> for InternedString {
-    #[inline(always)]
-    fn eq(&self, other: & &'a str) -> bool {
-        PartialEq::eq(&self.string[..], *other)
+impl LazyTokenStream {
+    pub fn new() -> Self {
+        LazyTokenStream(Cell::new(None))
     }
-    #[inline(always)]
-    fn ne(&self, other: & &'a str) -> bool {
-        PartialEq::ne(&self.string[..], *other)
-    }
-}
 
-impl<'a> PartialEq<InternedString > for &'a str {
-    #[inline(always)]
-    fn eq(&self, other: &InternedString) -> bool {
-        PartialEq::eq(*self, &other.string[..])
-    }
-    #[inline(always)]
-    fn ne(&self, other: &InternedString) -> bool {
-        PartialEq::ne(*self, &other.string[..])
+    pub fn force<F: FnOnce() -> TokenStream>(&self, f: F) -> TokenStream {
+        let mut opt_stream = self.0.take();
+        if opt_stream.is_none() {
+            opt_stream = Some(f());
+        }
+        self.0.set(opt_stream.clone());
+        opt_stream.clone().unwrap()
     }
 }
 
-impl Decodable for InternedString {
-    fn decode<D: Decoder>(d: &mut D) -> Result<InternedString, D::Error> {
-        Ok(get_name(get_ident_interner().intern(&try!(d.read_str())[..])))
+impl Encodable for LazyTokenStream {
+    fn encode<S: Encoder>(&self, _: &mut S) -> Result<(), S::Error> {
+        Ok(())
     }
 }
 
-impl Encodable for InternedString {
-    fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
-        s.emit_str(&self.string)
+impl Decodable for LazyTokenStream {
+    fn decode<D: Decoder>(_: &mut D) -> Result<LazyTokenStream, D::Error> {
+        Ok(LazyTokenStream::new())
     }
 }
 
-/// Returns the string contents of a name, using the thread-local interner.
-#[inline]
-pub fn get_name(name: ast::Name) -> InternedString {
-    let interner = get_ident_interner();
-    InternedString::new_from_rc_str(interner.get(name))
-}
-
-/// Returns the string contents of an identifier, using the thread-local
-/// interner.
-#[inline]
-pub fn get_ident(ident: ast::Ident) -> InternedString {
-    get_name(ident.name)
-}
-
-/// Interns and returns the string contents of an identifier, using the
-/// thread-local interner.
-#[inline]
-pub fn intern_and_get_ident(s: &str) -> InternedString {
-    get_name(intern(s))
-}
-
-/// Maps a string to its interned representation.
-#[inline]
-pub fn intern(s: &str) -> ast::Name {
-    get_ident_interner().intern(s)
-}
-
-/// gensym's a new usize, using the current interner.
-#[inline]
-pub fn gensym(s: &str) -> ast::Name {
-    get_ident_interner().gensym(s)
-}
-
-/// Maps a string to an identifier with an empty syntax context.
-#[inline]
-pub fn str_to_ident(s: &str) -> ast::Ident {
-    ast::Ident::new(intern(s))
+impl ::std::hash::Hash for LazyTokenStream {
+    fn hash<H: ::std::hash::Hasher>(&self, _hasher: &mut H) {}
 }
 
-/// Maps a string to a gensym'ed identifier.
-#[inline]
-pub fn gensym_ident(s: &str) -> ast::Ident {
-    ast::Ident::new(gensym(s))
-}
-
-// create a fresh name that maps to the same string as the old one.
-// note that this guarantees that str_ptr_eq(ident_to_string(src),interner_get(fresh_name(src)));
-// that is, that the new name and the old one are connected to ptr_eq strings.
-pub fn fresh_name(src: &ast::Ident) -> ast::Name {
-    let interner = get_ident_interner();
-    interner.gensym_copy(src.name)
-    // following: debug version. Could work in final except that it's incompatible with
-    // good error messages and uses of struct names in ambiguous could-be-binding
-    // locations. Also definitely destroys the guarantee given above about ptr_eq.
-    /*let num = rand::thread_rng().gen_uint_range(0,0xffff);
-    gensym(format!("{}_{}",ident_to_string(src),num))*/
-}
-
-// create a fresh mark.
-pub fn fresh_mark() -> ast::Mrk {
-    gensym("mark").usize() as u32
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use ast;
-    use ext::mtwt;
-
-    fn mark_ident(id : ast::Ident, m : ast::Mrk) -> ast::Ident {
-        ast::Ident { name: id.name, ctxt:mtwt::apply_mark(m, id.ctxt) }
+fn prepend_attrs(sess: &ParseSess,
+                 attrs: &[ast::Attribute],
+                 tokens: Option<&tokenstream::TokenStream>,
+                 span: syntax_pos::Span)
+    -> Option<tokenstream::TokenStream>
+{
+    let tokens = match tokens {
+        Some(tokens) => tokens,
+        None => return None,
+    };
+    if attrs.len() == 0 {
+        return Some(tokens.clone())
     }
-
-    #[test] fn mtwt_token_eq_test() {
-        assert!(Gt.mtwt_eq(&Gt));
-        let a = str_to_ident("bac");
-        let a1 = mark_ident(a,92);
-        assert!(Ident(a, ModName).mtwt_eq(&Ident(a1, Plain)));
+    let mut builder = tokenstream::TokenStreamBuilder::new();
+    for attr in attrs {
+        assert_eq!(attr.style, ast::AttrStyle::Outer,
+                   "inner attributes should prevent cached tokens from existing");
+        // FIXME: Avoid this pretty-print + reparse hack as bove
+        let name = "<macro expansion>".to_owned();
+        let source = pprust::attr_to_string(attr);
+        let stream = parse_stream_from_source_str(name, source, sess, Some(span));
+        builder.push(stream);
     }
+    builder.push(tokens.clone());
+    Some(builder.build())
 }