rustc-ap-proc_macro 40.0.0

Manually published version of the package proc_macro in the rust-lang/rust repository from commit 3ec5a99aaa0084d97a9e845b34fdf03d1462c475
// 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.

//! # Quasiquoter
//! This file contains the implementation internals of the quasiquoter provided by `quote!`.

//! This quasiquoter uses macros 2.0 hygiene to reliably access
//! items from `proc_macro`, to build a `proc_macro::TokenStream`.

use {Delimiter, Literal, Spacing, Span, Term, TokenNode, TokenStream, TokenTree};

use syntax::ext::base::{ExtCtxt, ProcMacro};
use syntax::parse::token;
use syntax::tokenstream;

pub struct Quoter;

pub fn unquote<T: Into<TokenStream> + Clone>(tokens: &T) -> TokenStream {
    T::into(tokens.clone())
}

pub trait Quote {
    fn quote(self) -> TokenStream;
}

macro_rules! quote_tok {
    (,) => { TokenNode::Op(',', Spacing::Alone) };
    (.) => { TokenNode::Op('.', Spacing::Alone) };
    (:) => { TokenNode::Op(':', Spacing::Alone) };
    (::) => {
        [
            TokenNode::Op(':', Spacing::Joint),
            TokenNode::Op(':', Spacing::Alone)
        ].iter().cloned().collect::<TokenStream>()
    };
    (!) => { TokenNode::Op('!', Spacing::Alone) };
    (<) => { TokenNode::Op('<', Spacing::Alone) };
    (>) => { TokenNode::Op('>', Spacing::Alone) };
    (_) => { TokenNode::Op('_', Spacing::Alone) };
    (0) => { TokenNode::Literal(::Literal::integer(0)) };
    (&) => { TokenNode::Op('&', Spacing::Alone) };
    ($i:ident) => { TokenNode::Term(Term::intern(stringify!($i))) };
}

macro_rules! quote_tree {
    ((unquote $($t:tt)*)) => { $($t)* };
    ((quote $($t:tt)*)) => { ($($t)*).quote() };
    (($($t:tt)*)) => { TokenNode::Group(Delimiter::Parenthesis, quote!($($t)*)) };
    ([$($t:tt)*]) => { TokenNode::Group(Delimiter::Bracket, quote!($($t)*)) };
    ({$($t:tt)*}) => { TokenNode::Group(Delimiter::Brace, quote!($($t)*)) };
    ($t:tt) => { quote_tok!($t) };
}

macro_rules! quote {
    () => { TokenStream::empty() };
    ($($t:tt)*) => {
        [
            $(TokenStream::from(quote_tree!($t)),)*
        ].iter().cloned().collect::<TokenStream>()
    };
}

impl ProcMacro for Quoter {
    fn expand<'cx>(&self, cx: &'cx mut ExtCtxt,
                   _: ::syntax_pos::Span,
                   stream: tokenstream::TokenStream)
                   -> tokenstream::TokenStream {
        let mut info = cx.current_expansion.mark.expn_info().unwrap();
        info.callee.allow_internal_unstable = true;
        cx.current_expansion.mark.set_expn_info(info);
        ::__internal::set_sess(cx, || TokenStream(stream).quote().0)
    }
}

impl<T: Quote> Quote for Option<T> {
    fn quote(self) -> TokenStream {
        match self {
            Some(t) => quote!(Some((quote t))),
            None => quote!(None),
        }
    }
}

impl Quote for TokenStream {
    fn quote(self) -> TokenStream {
        if self.is_empty() {
            return quote!(::TokenStream::empty());
        }
        let mut after_dollar = false;
        let tokens = self.into_iter().filter_map(|tree| {
            if after_dollar {
                after_dollar = false;
                match tree.kind {
                    TokenNode::Term(_) => {
                        return Some(quote!(::__internal::unquote(&(unquote tree)),));
                    }
                    TokenNode::Op('$', _) => {}
                    _ => panic!("`$` must be followed by an ident or `$` in `quote!`"),
                }
            } else if let TokenNode::Op('$', _) = tree.kind {
                after_dollar = true;
                return None;
            }

            Some(quote!(::TokenStream::from((quote tree)),))
        }).collect::<TokenStream>();

        if after_dollar {
            panic!("unexpected trailing `$` in `quote!`");
        }

        quote!([(unquote tokens)].iter().cloned().collect::<::TokenStream>())
    }
}

impl Quote for TokenTree {
    fn quote(self) -> TokenStream {
        quote!(::TokenTree { span: (quote self.span), kind: (quote self.kind) })
    }
}

impl Quote for TokenNode {
    fn quote(self) -> TokenStream {
        macro_rules! gen_match {
            ($($i:ident($($arg:ident),+)),*) => {
                match self {
                    $(TokenNode::$i($($arg),+) => quote! {
                        ::TokenNode::$i($((quote $arg)),+)
                    },)*
                }
            }
        }

        gen_match! { Op(op, kind), Group(delim, tokens), Term(term), Literal(lit) }
    }
}

impl Quote for char {
    fn quote(self) -> TokenStream {
        TokenNode::Literal(Literal::character(self)).into()
    }
}

impl<'a> Quote for &'a str {
    fn quote(self) -> TokenStream {
        TokenNode::Literal(Literal::string(self)).into()
    }
}

impl Quote for usize {
    fn quote(self) -> TokenStream {
        TokenNode::Literal(Literal::integer(self as i128)).into()
    }
}

impl Quote for Term {
    fn quote(self) -> TokenStream {
        quote!(::Term::intern((quote self.as_str())))
    }
}

impl Quote for Span {
    fn quote(self) -> TokenStream {
        quote!(::Span::def_site())
    }
}

macro_rules! literals {
    ($($i:ident),*; $($raw:ident),*) => {
        pub enum LiteralKind {
            $($i,)*
            $($raw(usize),)*
        }

        impl LiteralKind {
            pub fn with_contents_and_suffix(self, contents: Term, suffix: Option<Term>)
                                            -> Literal {
                let contents = contents.0;
                let suffix = suffix.map(|t| t.0);
                match self {
                    $(LiteralKind::$i => {
                        Literal(token::Literal(token::Lit::$i(contents), suffix))
                    })*
                    $(LiteralKind::$raw(n) => {
                        Literal(token::Literal(token::Lit::$raw(contents, n), suffix))
                    })*
                }
            }
        }

        impl Literal {
            fn kind_contents_and_suffix(self) -> (LiteralKind, Term, Option<Term>) {
                let (lit, suffix) = match self.0 {
                    token::Literal(lit, suffix) => (lit, suffix),
                    _ => panic!("unsupported literal {:?}", self.0),
                };

                let (kind, contents) = match lit {
                    $(token::Lit::$i(contents) => (LiteralKind::$i, contents),)*
                    $(token::Lit::$raw(contents, n) => (LiteralKind::$raw(n), contents),)*
                };
                (kind, Term(contents), suffix.map(Term))
            }
        }

        impl Quote for LiteralKind {
            fn quote(self) -> TokenStream {
                match self {
                    $(LiteralKind::$i => quote! {
                        ::__internal::LiteralKind::$i
                    },)*
                    $(LiteralKind::$raw(n) => quote! {
                        ::__internal::LiteralKind::$raw((quote n))
                    },)*
                }
            }
        }

        impl Quote for Literal {
            fn quote(self) -> TokenStream {
                let (kind, contents, suffix) = self.kind_contents_and_suffix();
                quote! {
                    (quote kind).with_contents_and_suffix((quote contents), (quote suffix))
                }
            }
        }
    }
}

literals!(Byte, Char, Float, Str_, Integer, ByteStr; StrRaw, ByteStrRaw);

impl Quote for Delimiter {
    fn quote(self) -> TokenStream {
        macro_rules! gen_match {
            ($($i:ident),*) => {
                match self {
                    $(Delimiter::$i => { quote!(::Delimiter::$i) })*
                }
            }
        }

        gen_match!(Parenthesis, Brace, Bracket, None)
    }
}

impl Quote for Spacing {
    fn quote(self) -> TokenStream {
        macro_rules! gen_match {
            ($($i:ident),*) => {
                match self {
                    $(Spacing::$i => { quote!(::Spacing::$i) })*
                }
            }
        }

        gen_match!(Alone, Joint)
    }
}