2017-03-17 23:41:09 +00:00
|
|
|
// 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
|
2017-06-05 01:41:33 +00:00
|
|
|
//! This file contains the implementation internals of the quasiquoter provided by `quote!`.
|
|
|
|
|
2017-11-05 20:21:05 +02:00
|
|
|
//! This quasiquoter uses macros 2.0 hygiene to reliably access
|
|
|
|
//! items from `proc_macro`, to build a `proc_macro::TokenStream`.
|
2017-03-17 23:41:09 +00:00
|
|
|
|
2018-04-02 08:19:32 -07:00
|
|
|
use {Delimiter, Literal, Spacing, Span, Term, Op, Group, TokenStream, TokenTree};
|
2017-11-05 18:25:45 +02:00
|
|
|
|
2017-03-17 23:41:09 +00:00
|
|
|
use syntax::ext::base::{ExtCtxt, ProcMacro};
|
2017-11-05 18:25:45 +02:00
|
|
|
use syntax::parse::token;
|
|
|
|
use syntax::tokenstream;
|
2017-03-17 23:41:09 +00:00
|
|
|
|
|
|
|
pub struct Quoter;
|
|
|
|
|
2017-11-05 20:21:05 +02:00
|
|
|
pub fn unquote<T: Into<TokenStream> + Clone>(tokens: &T) -> TokenStream {
|
2018-04-02 08:19:32 -07:00
|
|
|
tokens.clone().into()
|
2017-03-17 23:41:09 +00:00
|
|
|
}
|
|
|
|
|
2017-06-05 01:41:33 +00:00
|
|
|
pub trait Quote {
|
2017-11-05 18:25:45 +02:00
|
|
|
fn quote(self) -> TokenStream;
|
2017-03-17 23:41:09 +00:00
|
|
|
}
|
|
|
|
|
2018-04-02 08:19:32 -07:00
|
|
|
macro_rules! tt2ts {
|
|
|
|
($e:expr) => (TokenStream::from(TokenTree::from($e)))
|
|
|
|
}
|
|
|
|
|
2017-03-17 23:41:09 +00:00
|
|
|
macro_rules! quote_tok {
|
2018-04-02 08:19:32 -07:00
|
|
|
(,) => { tt2ts!(Op::new(',', Spacing::Alone)) };
|
|
|
|
(.) => { tt2ts!(Op::new('.', Spacing::Alone)) };
|
|
|
|
(:) => { tt2ts!(Op::new(':', Spacing::Alone)) };
|
|
|
|
(|) => { tt2ts!(Op::new('|', Spacing::Alone)) };
|
2017-11-05 18:25:45 +02:00
|
|
|
(::) => {
|
|
|
|
[
|
2018-04-02 08:19:32 -07:00
|
|
|
TokenTree::from(Op::new(':', Spacing::Joint)),
|
|
|
|
TokenTree::from(Op::new(':', Spacing::Alone)),
|
|
|
|
].iter()
|
|
|
|
.cloned()
|
|
|
|
.map(|mut x| {
|
|
|
|
x.set_span(Span::def_site());
|
|
|
|
x
|
|
|
|
})
|
|
|
|
.collect::<TokenStream>()
|
2017-11-05 18:25:45 +02:00
|
|
|
};
|
2018-04-02 08:19:32 -07:00
|
|
|
(!) => { tt2ts!(Op::new('!', Spacing::Alone)) };
|
|
|
|
(<) => { tt2ts!(Op::new('<', Spacing::Alone)) };
|
|
|
|
(>) => { tt2ts!(Op::new('>', Spacing::Alone)) };
|
|
|
|
(_) => { tt2ts!(Op::new('_', Spacing::Alone)) };
|
|
|
|
(0) => { tt2ts!(Literal::i8_unsuffixed(0)) };
|
|
|
|
(&) => { tt2ts!(Op::new('&', Spacing::Alone)) };
|
|
|
|
($i:ident) => { tt2ts!(Term::new(stringify!($i), Span::def_site())) };
|
2017-03-17 23:41:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
macro_rules! quote_tree {
|
2017-11-05 18:25:45 +02:00
|
|
|
((unquote $($t:tt)*)) => { $($t)* };
|
2017-03-17 23:41:09 +00:00
|
|
|
((quote $($t:tt)*)) => { ($($t)*).quote() };
|
2018-04-02 08:19:32 -07:00
|
|
|
(($($t:tt)*)) => { tt2ts!(Group::new(Delimiter::Parenthesis, quote!($($t)*))) };
|
|
|
|
([$($t:tt)*]) => { tt2ts!(Group::new(Delimiter::Bracket, quote!($($t)*))) };
|
|
|
|
({$($t:tt)*}) => { tt2ts!(Group::new(Delimiter::Brace, quote!($($t)*))) };
|
2017-11-05 18:25:45 +02:00
|
|
|
($t:tt) => { quote_tok!($t) };
|
2017-03-17 23:41:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
macro_rules! quote {
|
|
|
|
() => { TokenStream::empty() };
|
2017-11-05 18:25:45 +02:00
|
|
|
($($t:tt)*) => {
|
2018-04-02 08:19:32 -07:00
|
|
|
[$(quote_tree!($t),)*].iter()
|
|
|
|
.cloned()
|
|
|
|
.flat_map(|x| x.into_iter())
|
|
|
|
.collect::<TokenStream>()
|
2017-11-05 18:25:45 +02:00
|
|
|
};
|
2017-03-17 23:41:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ProcMacro for Quoter {
|
2017-11-05 18:25:45 +02:00
|
|
|
fn expand<'cx>(&self, cx: &'cx mut ExtCtxt,
|
|
|
|
_: ::syntax_pos::Span,
|
|
|
|
stream: tokenstream::TokenStream)
|
|
|
|
-> tokenstream::TokenStream {
|
2017-03-17 23:41:09 +00:00
|
|
|
let mut info = cx.current_expansion.mark.expn_info().unwrap();
|
|
|
|
info.callee.allow_internal_unstable = true;
|
|
|
|
cx.current_expansion.mark.set_expn_info(info);
|
2017-11-05 20:21:05 +02:00
|
|
|
::__internal::set_sess(cx, || TokenStream(stream).quote().0)
|
2017-03-17 23:41:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T: Quote> Quote for Option<T> {
|
2017-11-05 18:25:45 +02:00
|
|
|
fn quote(self) -> TokenStream {
|
|
|
|
match self {
|
|
|
|
Some(t) => quote!(Some((quote t))),
|
2017-03-17 23:41:09 +00:00
|
|
|
None => quote!(None),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Quote for TokenStream {
|
2017-11-05 18:25:45 +02:00
|
|
|
fn quote(self) -> TokenStream {
|
2017-11-05 20:21:05 +02:00
|
|
|
if self.is_empty() {
|
|
|
|
return quote!(::TokenStream::empty());
|
|
|
|
}
|
2017-11-05 18:25:45 +02:00
|
|
|
let mut after_dollar = false;
|
2017-11-05 20:21:05 +02:00
|
|
|
let tokens = self.into_iter().filter_map(|tree| {
|
|
|
|
if after_dollar {
|
|
|
|
after_dollar = false;
|
2018-04-02 08:19:32 -07:00
|
|
|
match tree {
|
|
|
|
TokenTree::Term(_) => {
|
|
|
|
let tree = TokenStream::from(tree);
|
2017-11-05 20:21:05 +02:00
|
|
|
return Some(quote!(::__internal::unquote(&(unquote tree)),));
|
2017-03-17 23:41:09 +00:00
|
|
|
}
|
2018-04-02 08:19:32 -07:00
|
|
|
TokenTree::Op(ref tt) if tt.op() == '$' => {}
|
2017-11-05 20:21:05 +02:00
|
|
|
_ => panic!("`$` must be followed by an ident or `$` in `quote!`"),
|
2017-03-17 23:41:09 +00:00
|
|
|
}
|
2018-04-02 08:19:32 -07:00
|
|
|
} else if let TokenTree::Op(tt) = tree {
|
|
|
|
if tt.op() == '$' {
|
|
|
|
after_dollar = true;
|
|
|
|
return None;
|
|
|
|
}
|
2017-11-05 20:21:05 +02:00
|
|
|
}
|
2017-03-17 23:41:09 +00:00
|
|
|
|
2017-11-05 20:21:05 +02:00
|
|
|
Some(quote!(::TokenStream::from((quote tree)),))
|
2018-04-02 08:19:32 -07:00
|
|
|
}).flat_map(|t| t.into_iter()).collect::<TokenStream>();
|
2017-11-05 18:25:45 +02:00
|
|
|
|
|
|
|
if after_dollar {
|
|
|
|
panic!("unexpected trailing `$` in `quote!`");
|
2017-03-17 23:41:09 +00:00
|
|
|
}
|
2017-11-05 18:25:45 +02:00
|
|
|
|
2018-04-02 08:19:32 -07:00
|
|
|
quote!(
|
|
|
|
[(unquote tokens)].iter()
|
|
|
|
.cloned()
|
|
|
|
.flat_map(|x| x.into_iter())
|
|
|
|
.collect::<::TokenStream>()
|
|
|
|
)
|
2017-03-17 23:41:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Quote for TokenTree {
|
2017-11-05 18:25:45 +02:00
|
|
|
fn quote(self) -> TokenStream {
|
2018-04-02 08:19:32 -07:00
|
|
|
match self {
|
|
|
|
TokenTree::Op(tt) => quote!(::TokenTree::Op( (quote tt) )),
|
|
|
|
TokenTree::Group(tt) => quote!(::TokenTree::Group( (quote tt) )),
|
|
|
|
TokenTree::Term(tt) => quote!(::TokenTree::Term( (quote tt) )),
|
|
|
|
TokenTree::Literal(tt) => quote!(::TokenTree::Literal( (quote tt) )),
|
|
|
|
}
|
2017-11-05 20:21:05 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-02 08:19:32 -07:00
|
|
|
impl Quote for char {
|
2017-11-05 20:21:05 +02:00
|
|
|
fn quote(self) -> TokenStream {
|
2018-04-02 08:19:32 -07:00
|
|
|
TokenTree::from(Literal::character(self)).into()
|
|
|
|
}
|
|
|
|
}
|
2017-11-05 20:21:05 +02:00
|
|
|
|
2018-04-02 08:19:32 -07:00
|
|
|
impl<'a> Quote for &'a str {
|
|
|
|
fn quote(self) -> TokenStream {
|
|
|
|
TokenTree::from(Literal::string(self)).into()
|
2017-11-05 20:21:05 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-12 19:50:53 +10:00
|
|
|
impl Quote for u16 {
|
2017-11-05 20:21:05 +02:00
|
|
|
fn quote(self) -> TokenStream {
|
2018-04-12 19:50:53 +10:00
|
|
|
TokenTree::from(Literal::u16_unsuffixed(self)).into()
|
2017-03-17 23:41:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-02 08:19:32 -07:00
|
|
|
impl Quote for Group {
|
2017-11-05 18:25:45 +02:00
|
|
|
fn quote(self) -> TokenStream {
|
2018-04-02 08:19:32 -07:00
|
|
|
quote!(::Group::new((quote self.delimiter()), (quote self.stream())))
|
2017-03-17 23:41:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-02 08:19:32 -07:00
|
|
|
impl Quote for Op {
|
2017-11-05 18:25:45 +02:00
|
|
|
fn quote(self) -> TokenStream {
|
2018-04-02 08:19:32 -07:00
|
|
|
quote!(::Op::new((quote self.op()), (quote self.spacing())))
|
2017-03-17 23:41:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-05 18:25:45 +02:00
|
|
|
impl Quote for Term {
|
|
|
|
fn quote(self) -> TokenStream {
|
2018-04-09 23:49:25 +02:00
|
|
|
quote!(::Term::new((quote self.sym.as_str()), (quote self.span())))
|
2017-03-17 23:41:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Quote for Span {
|
2017-11-05 18:25:45 +02:00
|
|
|
fn quote(self) -> TokenStream {
|
2017-11-14 21:44:43 -08:00
|
|
|
quote!(::Span::def_site())
|
2017-03-17 23:41:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-05 20:21:05 +02:00
|
|
|
macro_rules! literals {
|
|
|
|
($($i:ident),*; $($raw:ident),*) => {
|
|
|
|
pub enum LiteralKind {
|
|
|
|
$($i,)*
|
2018-04-12 19:50:53 +10:00
|
|
|
$($raw(u16),)*
|
2017-11-05 20:21:05 +02:00
|
|
|
}
|
2017-03-17 23:41:09 +00:00
|
|
|
|
2017-11-05 20:21:05 +02:00
|
|
|
impl LiteralKind {
|
|
|
|
pub fn with_contents_and_suffix(self, contents: Term, suffix: Option<Term>)
|
|
|
|
-> Literal {
|
2018-04-02 08:19:32 -07:00
|
|
|
let sym = contents.sym;
|
|
|
|
let suffix = suffix.map(|t| t.sym);
|
2017-11-05 20:21:05 +02:00
|
|
|
match self {
|
|
|
|
$(LiteralKind::$i => {
|
2018-04-02 08:19:32 -07:00
|
|
|
Literal {
|
2018-04-06 15:20:57 -07:00
|
|
|
lit: token::Lit::$i(sym),
|
|
|
|
suffix,
|
2018-04-02 08:19:32 -07:00
|
|
|
span: contents.span,
|
|
|
|
}
|
2017-11-05 20:21:05 +02:00
|
|
|
})*
|
|
|
|
$(LiteralKind::$raw(n) => {
|
2018-04-02 08:19:32 -07:00
|
|
|
Literal {
|
2018-04-06 15:20:57 -07:00
|
|
|
lit: token::Lit::$raw(sym, n),
|
|
|
|
suffix,
|
2018-04-02 08:19:32 -07:00
|
|
|
span: contents.span,
|
|
|
|
}
|
2017-11-05 20:21:05 +02:00
|
|
|
})*
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Literal {
|
|
|
|
fn kind_contents_and_suffix(self) -> (LiteralKind, Term, Option<Term>) {
|
2018-04-06 15:20:57 -07:00
|
|
|
let (kind, contents) = match self.lit {
|
2017-11-05 20:21:05 +02:00
|
|
|
$(token::Lit::$i(contents) => (LiteralKind::$i, contents),)*
|
|
|
|
$(token::Lit::$raw(contents, n) => (LiteralKind::$raw(n), contents),)*
|
|
|
|
};
|
2018-04-06 15:20:57 -07:00
|
|
|
let suffix = self.suffix.map(|sym| Term::new(&sym.as_str(), self.span()));
|
2018-04-02 08:19:32 -07:00
|
|
|
(kind, Term::new(&contents.as_str(), self.span()), suffix)
|
2017-11-05 20:21:05 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Quote for LiteralKind {
|
|
|
|
fn quote(self) -> TokenStream {
|
|
|
|
match self {
|
|
|
|
$(LiteralKind::$i => quote! {
|
|
|
|
::__internal::LiteralKind::$i
|
2017-11-05 18:25:45 +02:00
|
|
|
},)*
|
2017-11-05 20:21:05 +02:00
|
|
|
$(LiteralKind::$raw(n) => quote! {
|
|
|
|
::__internal::LiteralKind::$raw((quote n))
|
2017-11-05 18:25:45 +02:00
|
|
|
},)*
|
2017-03-17 23:41:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-05 20:21:05 +02:00
|
|
|
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))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-03-17 23:41:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-05 20:21:05 +02:00
|
|
|
literals!(Byte, Char, Float, Str_, Integer, ByteStr; StrRaw, ByteStrRaw);
|
|
|
|
|
2017-11-05 18:25:45 +02:00
|
|
|
impl Quote for Delimiter {
|
|
|
|
fn quote(self) -> TokenStream {
|
2017-03-17 23:41:09 +00:00
|
|
|
macro_rules! gen_match {
|
2017-11-05 20:21:05 +02:00
|
|
|
($($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),*) => {
|
2017-11-05 18:25:45 +02:00
|
|
|
match self {
|
2017-11-05 20:21:05 +02:00
|
|
|
$(Spacing::$i => { quote!(::Spacing::$i) })*
|
2017-03-17 23:41:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-05 20:21:05 +02:00
|
|
|
gen_match!(Alone, Joint)
|
2017-03-17 23:41:09 +00:00
|
|
|
}
|
|
|
|
}
|