diff options
| author | Jeffrey Seyfried <jeffrey.seyfried@gmail.com> | 2017-03-14 22:04:46 +0000 |
|---|---|---|
| committer | Jeffrey Seyfried <jeffrey.seyfried@gmail.com> | 2017-03-15 23:05:02 +0000 |
| commit | ce616a7d6ad838aacd080b47566c15e82ad8dd6d (patch) | |
| tree | 9f91891e12094eb0d0e96e564711c8905859f3d4 /src/libproc_macro_plugin | |
| parent | d9cf601ae822d07412a79aeedeabb802aa94cb34 (diff) | |
| download | rust-ce616a7d6ad838aacd080b47566c15e82ad8dd6d.tar.gz rust-ce616a7d6ad838aacd080b47566c15e82ad8dd6d.zip | |
Improve the `TokenStream` quoter.
Diffstat (limited to 'src/libproc_macro_plugin')
| -rw-r--r-- | src/libproc_macro_plugin/lib.rs | 78 | ||||
| -rw-r--r-- | src/libproc_macro_plugin/quote.rs (renamed from src/libproc_macro_plugin/qquote.rs) | 26 |
2 files changed, 55 insertions, 49 deletions
diff --git a/src/libproc_macro_plugin/lib.rs b/src/libproc_macro_plugin/lib.rs index e9042909576..a6dad641253 100644 --- a/src/libproc_macro_plugin/lib.rs +++ b/src/libproc_macro_plugin/lib.rs @@ -13,62 +13,64 @@ //! A library for procedural macro writers. //! //! ## Usage -//! This crate provides the `qquote!` macro for syntax creation. +//! This crate provides the `quote!` macro for syntax creation. //! -//! The `qquote!` macro uses the crate `syntax`, so users must declare `extern crate syntax;` +//! The `quote!` macro uses the crate `syntax`, so users must declare `extern crate syntax;` //! at the crate root. This is a temporary solution until we have better hygiene. //! //! ## Quasiquotation //! //! The quasiquoter creates output that, when run, constructs the tokenstream specified as -//! input. For example, `qquote!(5 + 5)` will produce a program, that, when run, will +//! input. For example, `quote!(5 + 5)` will produce a program, that, when run, will //! construct the TokenStream `5 | + | 5`. //! //! ### Unquoting //! -//! Unquoting is currently done as `unquote`, and works by taking the single next -//! TokenTree in the TokenStream as the unquoted term. Ergonomically, `unquote(foo)` works -//! fine, but `unquote foo` is also supported. +//! Unquoting is done with `$`, and works by taking the single next ident as the unquoted term. +//! To quote `$` itself, use `$$`. //! -//! A simple example might be: +//! A simple example is: //! //!``` //!fn double(tmp: TokenStream) -> TokenStream { -//! qquote!(unquote(tmp) * 2) +//! quote!($tmp * 2) //!} //!``` //! -//! ### Large Example: Implementing Scheme's `cond` +//! ### Large example: Scheme's `cond` //! -//! Below is the full implementation of Scheme's `cond` operator. +//! Below is an example implementation of Scheme's `cond`. //! //! ``` -//! fn cond_rec(input: TokenStream) -> TokenStream { -//! if input.is_empty() { return quote!(); } -//! -//! let next = input.slice(0..1); -//! let rest = input.slice_from(1..); -//! -//! let clause : TokenStream = match next.maybe_delimited() { -//! Some(ts) => ts, -//! _ => panic!("Invalid input"), -//! }; -//! -//! // clause is ([test]) [rhs] -//! if clause.len() < 2 { panic!("Invalid macro usage in cond: {:?}", clause) } -//! -//! let test: TokenStream = clause.slice(0..1); -//! let rhs: TokenStream = clause.slice_from(1..); -//! -//! if ident_eq(&test[0], str_to_ident("else")) || rest.is_empty() { -//! quote!({unquote(rhs)}) -//! } else { -//! quote!({if unquote(test) { unquote(rhs) } else { cond!(unquote(rest)) } }) -//! } +//! fn cond(input: TokenStream) -> TokenStream { +//! let mut conds = Vec::new(); +//! let mut input = input.trees().peekable(); +//! while let Some(tree) = input.next() { +//! let mut cond = match tree { +//! TokenTree::Delimited(_, ref delimited) => delimited.stream(), +//! _ => panic!("Invalid input"), +//! }; +//! let mut trees = cond.trees(); +//! let test = trees.next(); +//! let rhs = trees.collect::<TokenStream>(); +//! if rhs.is_empty() { +//! panic!("Invalid macro usage in cond: {}", cond); +//! } +//! let is_else = match test { +//! Some(TokenTree::Token(_, Token::Ident(ident))) if ident.name == "else" => true, +//! _ => false, +//! }; +//! conds.push(if is_else || input.peek().is_none() { +//! quote!({ $rhs }) +//! } else { +//! let test = test.unwrap(); +//! quote!(if $test { $rhs } else) +//! }); +//! } +//! +//! conds.into_iter().collect() //! } //! ``` -//! - #![crate_name = "proc_macro_plugin"] #![unstable(feature = "rustc_private", issue = "27812")] #![feature(plugin_registrar)] @@ -87,8 +89,8 @@ extern crate rustc_plugin; extern crate syntax; extern crate syntax_pos; -mod qquote; -use qquote::qquote; +mod quote; +use quote::quote; use rustc_plugin::Registry; use syntax::ext::base::SyntaxExtension; @@ -99,6 +101,6 @@ use syntax::symbol::Symbol; #[plugin_registrar] pub fn plugin_registrar(reg: &mut Registry) { - reg.register_syntax_extension(Symbol::intern("qquote"), - SyntaxExtension::ProcMacro(Box::new(qquote))); + reg.register_syntax_extension(Symbol::intern("quote"), + SyntaxExtension::ProcMacro(Box::new(quote))); } diff --git a/src/libproc_macro_plugin/qquote.rs b/src/libproc_macro_plugin/quote.rs index 0276587ed52..ad71584b61a 100644 --- a/src/libproc_macro_plugin/qquote.rs +++ b/src/libproc_macro_plugin/quote.rs @@ -19,7 +19,7 @@ use syntax_pos::DUMMY_SP; use std::iter; -pub fn qquote<'cx>(stream: TokenStream) -> TokenStream { +pub fn quote<'cx>(stream: TokenStream) -> TokenStream { stream.quote() } @@ -72,28 +72,32 @@ impl Quote for TokenStream { return quote!(::syntax::tokenstream::TokenStream::empty()); } - struct Quote(iter::Peekable<tokenstream::Cursor>); + struct Quoter(iter::Peekable<tokenstream::Cursor>); - impl Iterator for Quote { + impl Iterator for Quoter { type Item = TokenStream; fn next(&mut self) -> Option<TokenStream> { - let is_unquote = match self.0.peek() { - Some(&TokenTree::Token(_, Token::Ident(ident))) if ident.name == "unquote" => { - self.0.next(); - true + let quoted_tree = if let Some(&TokenTree::Token(_, Token::Dollar)) = self.0.peek() { + self.0.next(); + match self.0.next() { + Some(tree @ TokenTree::Token(_, Token::Ident(..))) => Some(tree.into()), + Some(tree @ TokenTree::Token(_, Token::Dollar)) => Some(tree.quote()), + // FIXME(jseyfried): improve these diagnostics + Some(..) => panic!("`$` must be followed by an ident or `$` in `quote!`"), + None => panic!("unexpected trailing `$` in `quote!`"), } - _ => false, + } else { + self.0.next().as_ref().map(Quote::quote) }; - self.0.next().map(|tree| { - let quoted_tree = if is_unquote { tree.into() } else { tree.quote() }; + quoted_tree.map(|quoted_tree| { quote!(::syntax::tokenstream::TokenStream::from((unquote quoted_tree)),) }) } } - let quoted = Quote(self.trees().peekable()).collect::<TokenStream>(); + let quoted = Quoter(self.trees().peekable()).collect::<TokenStream>(); quote!([(unquote quoted)].iter().cloned().collect::<::syntax::tokenstream::TokenStream>()) } } |
