├── .gitignore ├── .travis.yml ├── Cargo.toml ├── LICENSE ├── tests └── tests.rs ├── README.md └── src ├── lib.rs └── parser_any_macro.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | sudo: false 3 | rust: nightly 4 | 5 | matrix: 6 | fast_finish: true 7 | include: 8 | - env: TARGET=x86_64-unknown-linux-gnu 9 | name: "x86_64-unknown-linux-gnu" 10 | - env: TARGET=x86_64-apple-darwin 11 | name: "x86_64-apple-darwin" 12 | os: osx 13 | osx_image: xcode9.4 14 | 15 | script: cargo test 16 | 17 | notifications: 18 | email: 19 | on_success: never 20 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "interpolate_idents" 3 | version = "0.2.9" 4 | authors = ["Skyler Lipthay "] 5 | description = "Useable macro identifier concatenation plugin" 6 | readme = "README.md" 7 | keywords = ["macro", "identifier", "concatenation"] 8 | license = "MIT" 9 | repository = "https://github.com/SkylerLipthay/interpolate_idents" 10 | 11 | [lib] 12 | name = "interpolate_idents" 13 | plugin = true -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Skyler Lipthay 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /tests/tests.rs: -------------------------------------------------------------------------------- 1 | #![feature(plugin)] 2 | #![plugin(interpolate_idents)] 3 | 4 | macro_rules! define_foo { 5 | ($x:ident) => ( interpolate_idents! { 6 | fn [foo_ $x _1]() -> u32 { 1 } 7 | 8 | struct [Foo $x] { [$x _30]: u32 } 9 | impl [Foo $x] { 10 | pub fn new() -> [Foo $x] { 11 | [Foo $x] { [$x _30]: 30 } 12 | } 13 | } 14 | } ) 15 | } 16 | 17 | define_foo!(bar); 18 | 19 | #[test] 20 | fn test_macro() { 21 | assert_eq!(foo_bar_1(), 1); 22 | assert_eq!(Foobar::new().bar_30, 30); 23 | } 24 | 25 | macro_rules! define_brackets { 26 | () => ( interpolate_idents! { 27 | fn brackets(data: &[i32; 1]) -> Vec { 28 | let mut b: Vec = vec![]; 29 | let c: Vec = vec![1, 2, 3]; 30 | let d: Vec = vec![1; 25]; 31 | b.push(c[1]); 32 | b.push(d[1]); 33 | b.push(data[0]); 34 | b 35 | } 36 | } ) 37 | } 38 | 39 | define_brackets!(); 40 | 41 | #[test] 42 | fn test_brackets() { 43 | let data = [1; 1]; 44 | assert_eq!(brackets(&data), vec![2, 1, 1]); 45 | } 46 | 47 | 48 | macro_rules! define_underscore_idents { 49 | ($x:ident) => ( interpolate_idents! { 50 | fn [_ $x]() -> u32 { 1 } 51 | fn [$x _]() -> u32 { 2 } 52 | fn [_ $x _ $x _]() -> u32 { 3 } 53 | } ) 54 | } 55 | 56 | define_underscore_idents!(bar); 57 | 58 | #[test] 59 | fn test_underscores() { 60 | assert_eq!(_bar(), 1); 61 | assert_eq!(bar_(), 2); 62 | assert_eq!(_bar_bar_(), 3); 63 | } 64 | 65 | macro_rules! define_attributes { 66 | ($x:ident) => ( interpolate_idents! { 67 | #[inline] 68 | fn [_ $x]() -> u32 { 1 } 69 | 70 | #[allow(unreachable_code)] 71 | fn [_ $x _2]() -> u32 { return 1; 2 } 72 | } ) 73 | } 74 | 75 | define_attributes!(attr); 76 | 77 | #[test] 78 | fn test_attributes() { 79 | assert_eq!(_attr(), 1); 80 | } 81 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # interpolate_idents 2 | 3 | ## Deprecated 4 | 5 | This crate has been officially deprecated. 6 | 7 | Because of the introduction of stable procedural macro support in Rust 1.30.0, it is now possible to accomplish what this crate set out to accomplish using entirely stable code. Please try @dtolnay's [`paste`](https://crates.io/crates/paste) crate to see if it can fulfill all of your current use cases. `paste` can be used in stable production environments and is not nearly as prone to breakage between Rust versions. 8 | 9 | ## Summary 10 | 11 | [![Travis-CI Status]][travis] 12 | 13 | **Warning!** This crate uses a procedural macro (known today as a [compiler plugin](https://doc.rust-lang.org/book/compiler-plugins.html)) and can only be used with Rust's [nightly distribution](https://doc.rust-lang.org/book/nightly-rust.html). 14 | 15 | You cannot currently define a struct, enum, function, or field using 16 | `concat_idents!` due to the way macros are parsed by the Rust compiler. This 17 | will hopefully change in the future, but `interpolate_idents!` sloppily solves 18 | a side effect of the currently lacking macro system *today*. 19 | 20 | ```rust 21 | #![feature(plugin)] 22 | #![plugin(interpolate_idents)] 23 | 24 | macro_rules! make_fn { 25 | ($x:ident) => ( interpolate_idents! { 26 | fn [my_ $x _fn]() -> u32 { 1000 } 27 | } ) 28 | } 29 | ``` 30 | 31 | Now `make_fn!(favorite);` is equivalent to 32 | `fn my_favorite_fn() -> u32 { 1000 }`. 33 | 34 | In short, surround multiple space-separated identifiers (or macro identifer 35 | variables) with square brackets to concatenate the identifiers. Check 36 | `tests/tests.rs` for another example. 37 | 38 | This plugin was quickly hacked together. It is likely not performant and most 39 | certainly not readable. 40 | 41 | ## Crate upkeep 42 | 43 | I'm not actively developing on nightly, so I haven't been using this plugin too often. I understand that `libsyntax` is a fickle beast, so please file an issue or PR if `interpolate_idents` fails to compile on the latest nightly! 44 | 45 | [travis]: https://travis-ci.org/SkylerLipthay/interpolate_idents 46 | [Travis-CI Status]: https://travis-ci.org/SkylerLipthay/interpolate_idents.svg?branch=master 47 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(plugin_registrar, rustc_private)] 2 | 3 | extern crate smallvec; 4 | extern crate rustc; 5 | extern crate rustc_plugin; 6 | extern crate syntax; 7 | 8 | use rustc_plugin::Registry; 9 | use syntax::ast::Ident; 10 | use syntax::tokenstream::{TokenStream, TokenTree}; 11 | use syntax::source_map::Span; 12 | use syntax::ext::base::{ExtCtxt, MacResult}; 13 | use syntax::parse::token::{DelimToken, Token}; 14 | 15 | mod parser_any_macro; 16 | 17 | #[plugin_registrar] 18 | pub fn plugin_registrar(reg: &mut Registry) { 19 | reg.register_macro("interpolate_idents", interpolate_idents); 20 | } 21 | 22 | fn interpolate_idents<'a>(cx: &'a mut ExtCtxt, 23 | _: Span, 24 | tts: &[TokenTree]) -> Box { 25 | fn concat_idents(tts: TokenStream, delim: DelimToken) -> Option { 26 | match delim { 27 | DelimToken::Bracket => { 28 | let mut new_ident = String::new(); 29 | let mut new_span: Option = None; 30 | 31 | for token in tts.trees() { 32 | match token { 33 | TokenTree::Token(ref span, Token::Ident(ref ident, _)) => { 34 | match new_span { 35 | Some(ref mut s) => { *s = s.with_hi(span.hi()); }, 36 | None => { new_span = Some(span.clone()); }, 37 | } 38 | new_ident.push_str(&ident.name.as_str()); 39 | }, 40 | _ => return None, 41 | } 42 | } 43 | 44 | match new_span { 45 | Some(s) => { 46 | let new_ident = Ident::from_str(&new_ident[..]); 47 | Some(TokenTree::Token(s, Token::from_ast_ident(new_ident))) 48 | }, 49 | None => None 50 | } 51 | }, 52 | _ => None, 53 | } 54 | } 55 | 56 | fn map_tts(tts: TokenStream) -> TokenStream { 57 | // Ignore brackets preceded by a pound symbol (or a pound and an exclamation mark), so as 58 | // to allow attributes. 59 | let mut is_prev_pound = false; 60 | 61 | tts.trees().map(|t| { 62 | if is_prev_pound { 63 | is_prev_pound = false; 64 | return t.clone(); 65 | } 66 | 67 | if let TokenTree::Token(_, Token::Pound) = t { 68 | is_prev_pound = true; 69 | } 70 | 71 | match t { 72 | TokenTree::Delimited(s, d) => { 73 | match concat_idents(d.tts.clone().into(), d.delim) { 74 | Some(t) => t, 75 | None => { 76 | TokenTree::Delimited(s, syntax::tokenstream::Delimited { 77 | delim: d.delim, 78 | tts: map_tts(d.tts.into()).into(), 79 | }) 80 | }, 81 | } 82 | }, 83 | TokenTree::Token(..) => t.clone(), 84 | } 85 | }).collect() 86 | } 87 | 88 | let tts: TokenStream = map_tts(tts.iter().cloned().collect()); 89 | let tts: Vec = tts.trees().collect(); 90 | let parser = cx.new_parser_from_tts(&*tts); 91 | Box::new(parser_any_macro::ParserAnyMacro::new(parser)) as Box 92 | } 93 | -------------------------------------------------------------------------------- /src/parser_any_macro.rs: -------------------------------------------------------------------------------- 1 | // from src/libsyntax/ext/tt/macro_rules.rs 2 | 3 | use std::cell::RefCell; 4 | use smallvec::SmallVec; 5 | use syntax::{ 6 | parse::{ 7 | parser::Parser, 8 | token, 9 | }, 10 | ast, 11 | ptr::P, 12 | ext::base::MacResult 13 | }; 14 | 15 | macro_rules! panictry { 16 | ($e:expr) => ({ 17 | use std::result::Result::{Ok, Err}; 18 | use syntax::errors::FatalError; 19 | match $e { 20 | Ok(e) => e, 21 | Err(mut e) => { 22 | e.emit(); 23 | FatalError.raise(); 24 | } 25 | } 26 | }) 27 | } 28 | 29 | pub struct ParserAnyMacro<'a> { 30 | parser: RefCell>, 31 | } 32 | 33 | impl<'a> ParserAnyMacro<'a> { 34 | pub fn new(p: Parser<'a>) -> ParserAnyMacro<'a> { 35 | ParserAnyMacro { 36 | parser: RefCell::new(p) 37 | } 38 | } 39 | /// Make sure we don't have any tokens left to parse, so we don't 40 | /// silently drop anything. `allow_semi` is so that "optional" 41 | /// semicolons at the end of normal expressions aren't complained 42 | /// about e.g. the semicolon in `macro_rules! kapow { () => { 43 | /// panic!(); } }` doesn't get picked up by .parse_expr(), but it's 44 | /// allowed to be there. 45 | fn ensure_complete_parse(&self, allow_semi: bool) { 46 | let mut parser = self.parser.borrow_mut(); 47 | if allow_semi && parser.token == token::Semi { 48 | parser.bump(); 49 | } 50 | if parser.token != token::Eof { 51 | let token_str = parser.this_token_to_string(); 52 | let msg = format!("macro expansion ignores token `{}` and any \ 53 | following", 54 | token_str); 55 | let span = parser.span; 56 | parser.sess.span_diagnostic.span_err(span, &msg[..]); 57 | } 58 | } 59 | } 60 | 61 | impl<'a> MacResult for ParserAnyMacro<'a> { 62 | fn make_expr(self: Box>) -> Option> { 63 | let ret = panictry!(self.parser.borrow_mut().parse_expr()); 64 | self.ensure_complete_parse(true); 65 | Some(ret) 66 | } 67 | fn make_pat(self: Box>) -> Option> { 68 | let ret = panictry!(self.parser.borrow_mut().parse_pat(None)); 69 | self.ensure_complete_parse(false); 70 | Some(ret) 71 | } 72 | fn make_items(self: Box>) -> Option; 1]>> { 73 | let mut ret = SmallVec::new(); 74 | while let Some(item) = panictry!(self.parser.borrow_mut().parse_item()) { 75 | ret.push(item); 76 | } 77 | self.ensure_complete_parse(false); 78 | Some(ret) 79 | } 80 | 81 | fn make_impl_items(self: Box>) 82 | -> Option> { 83 | let mut ret = SmallVec::new(); 84 | loop { 85 | let mut parser = self.parser.borrow_mut(); 86 | match parser.token { 87 | token::Eof => break, 88 | _ => { 89 | let item = panictry!(parser.parse_item()).unwrap(); 90 | if let ast::ItemKind::Impl(_, _, _, _, _, _, ref vec) = item.node { 91 | if vec.len() != 1 { 92 | panic!("Expected 1 Implitem"); 93 | } 94 | ret.push(vec[0].clone()); 95 | } else { 96 | panic!("Expected Implitem"); 97 | } 98 | }, 99 | } 100 | } 101 | self.ensure_complete_parse(false); 102 | Some(ret) 103 | } 104 | 105 | fn make_stmts(self: Box>) 106 | -> Option> { 107 | let mut ret = SmallVec::new(); 108 | loop { 109 | let mut parser = self.parser.borrow_mut(); 110 | match parser.token { 111 | token::Eof => break, 112 | _ => match parser.parse_stmt() { 113 | Ok(maybe_stmt) => match maybe_stmt { 114 | Some(stmt) => ret.push(stmt), 115 | None => (), 116 | }, 117 | Err(_) => break, 118 | } 119 | } 120 | } 121 | self.ensure_complete_parse(false); 122 | Some(ret) 123 | } 124 | } 125 | --------------------------------------------------------------------------------