This crate provides Rust's quasi-quoting macro, intended to use in your proc-macro, to generate TokenStream
expanding variable interporation and expanding templates.
This macro is constructed based on proc_macro
crate.
Original quote!
macro syntax is fully supported. See quote's doc.
For backward compatibility, interporation rule is same as traditional
quote!
macro. The interporation is done with #var
(similar to the
variable $var
in macro_rules!
). Most variables in Syn
crate are
interporated using [::proc_quote::ToTokens
] trait.
Repetition is done using syntax like #(...)*
or #(...),*
. It repeats the
variables (#var
) inside this syntax, which implements
[::proc_quote::Repeat
].
#(...)*
- repeat ... with no separators. at least one variable should be
included in ...#(...),*
- same as before, but interporates with separator ','.The interporation rule is rough, so I implemented new 'template' syntax.
For example, the following code will not allowed, because #var1
cannot be
iterated double.
```
let var1 = vec!['a', 'b']; let var2 = vec![vec![1, 2], vec![3, 4]]; let tokens = quote!{ #(#(#var1 #var2)) }; asserteq!("'a' 1i32 'a' 2i32 'b' 3i32 'b' 4i32", tokens.tostring()); ```
Template syntax is proceedual-like syntax, which allows you to use structual statementsinside the macro.
This code iterates around #i
(with interporation), and emits i32
into
TokenStream
while the number meets the condition.
```
let i = vec![1, 2, 3]; let tokens = quote!{ #( #(if i > &2) { #i } )* }; asserteq!("3i32", tokens.tostring()); ```
The if-else and if-else-if is also allowed.
```
let i = vec![1, 2, 3]; let tokens = quote!{ #( #(if i > &2) { + #i } #(else) { - #i } )* }; asserteq!("- 1i32 - 2i32 + 3i32", tokens.tostring()); ```
```
let i = vec![1, 2, 3, 4, 5]; let tokens = quote!{ #( #(if i % &2 == 0) { + #i } #(else if i % &3 == 0) { - #i } #(else) { #i } )* }; asserteq!("1i32 + 2i32 - 3i32 + 4i32 5i32", tokens.tostring()); ```
For syntax iterates around the variable (like interporation), but it specifies which variable to iterate.
```
let v1 = vec![1, 2]; let v2 = vec!['a', 'b']; let tokens = quote!{ #(for i1 in &v1) { #(for i2 in &v2) { #i1 -> #i2 } } }; asserteq!("1i32 -> 'a' 1i32 -> 'b' 2i32 -> 'a' 2i32 -> 'b'", tokens.tostring()); ```
Internal loop can be replaced with interporation:
```
let v1 = vec![1, 2]; let v2 = vec!['a', 'b']; let tokens = quote!{ #(for i1 in &v1) { #( #i1 -> #v2 )* } }; asserteq!("1i32 -> 'a' 1i32 -> 'b' 2i32 -> 'a' 2i32 -> 'b'", tokens.tostring()); ```
You can also specify separator with for statement.
```
let v = vec![1, 2]; let tokens = quote!{ #(for i in v) | { #i } }; asserteq!("1i32 | 2i32", tokens.tostring()); ```
Interporation is not usable with variables binded in for syntax. For example,
```compile_fail
let v = vec![vec![1, 2], vec![3]]; let tokens = quote!{ #( #(for i in v) { #i } ),* }; asserteq!("1i32 2i32 , 3i32", tokens.tostring()); ```
will fail into error because no variables is available in the interporation syntax.
text
error: proc macro panicked
--> ***
|
6 | let tokens = quote!{
| ______________^
7 | | #(
8 | | #(for i in v) { #i }
9 | | )*
10 | | };
| |_^
|
= help: message: Iterative vals not found
In this case, you can use #(for i in #v)
syntax to specify which variable
to iterate with interporation:
```
let v = vec![vec![1, 2], vec![3]]; let tokens = quote!{ #( #(for i in #v) { #i } ),* }; asserteq!("1i32 2i32 , 3i32", tokens.tostring()); ```
```
let mut v = vec![1, 2].intoiter(); let tokens = quote!{ #(while v.next().issome()) { hello } }; asserteq!("hello hello", tokens.tostring()); ```
```
let mut v = vec![1, 2].intoiter(); let tokens = quote!{ #(while let Some(i) = v.next()) { #i } }; asserteq!("1i32 2i32", tokens.to_string()); ```
Same as 'for' syntax, the binded valiables in 'while' is not iteratable with interporation syntax. For example,
```compile_fail
let mut v = vec![1, 2].into_iter(); quote!{ #( #(while let Some(i) = v.next()) { #i } )* }; ```
will fail.
Let syntax bind new variables usable inside the block.
```
let v = vec![(1, 'a'), (2, 'b')]; let tokens = quote!{ #(for i in v), { #(let (n, c) = i) { #n -> #c } } }; asserteq!("1i32 -> 'a' , 2i32 -> 'b'", tokens.tostring()); ```
Here, #n
and #c
is not iteratable with interporation syntax.
You can place inline expression in quote!
macro.
```
let v = vec![1, 2]; let tokens = quote!{ #(for i in v){ #i -> #{ i.tostring() } } }; asserteq!("1i32 -> \"1\" 2i32 -> \"2\"", tokens.to_string()); ```
The following example will fail to compile because it does not understand which variable to be interpolated:
```compile_fail
let v = vec![1, 2]; let tokens = quote!{ #( #{ v.tostring() } )* }; asserteq!("\"1\" \"2\"", tokens.to_string()); ```
In this case, you can use #i
syntax in inline expression to specify which
variable to iterate with interporation syntax.
```
let v = vec![1, 2]; let tokens = quote!{ #( #{ #v.tostring() } )* }; asserteq!("\"1\" \"2\"", tokens.to_string()); ```
You can place arbitrary statement inside this macro. For example,
```
let v = vec![1, 2, 3]; let tokens = quote!{ #( #v #{ eprintln!("debug: {}", &v); } )* }; asserteq!("1i32 2i32 3i32", tokens.tostring()); ```
will print:
text
debug: 1
debug: 2
debug: 3
To be distinguishable, all statements have to end with ';'. For example, 'if' statement in inline statement syntax should placed with extra ';'.
```
let v = vec![1, 2, 3]; quote!{ #( #v #{ if v >= &2 { eprintln!("debug: {}", &v); } ; } )* }; ```
You can put control statement like break
or continue
in inline
statement, but it is a bit danger.
If you use break;
inside block (like { ... }
or ( ... )
), break
will
suddenly give up emitting whole group, and nothing will be emitted. For
example, the following code does not emit any group:
```
let v = vec![1, 2, 3]; let tokens = quote!{ #(for i in v) { #i // this is emitted once // The block is not emitted { #i #{ break; } } } }; asserteq!("1i32", tokens.tostring()); ```
break
also affects on interporation syntax like:
```
let v = vec![1, 2, 3]; let tokens = quote!{ #( #v #{ break; } ),* }; asserteq!("1i32", tokens.tostring()); ```
Unfortunately, break
will leak outside of quote!
macro. This is example
which the internal break
affects on 'for' loop, which is placed outer of
the quote!
macro.
```
let mut v = Vec::new(); for _ in 0..3 { let tokens = quote!{ #{ break; } }; v.push(tokens); } assert_eq!(v.len(), 0); ```
This crate provides quasi-quoting macros like quote.
This crate has backward-compatibility with original quote!
macro and also provides
new template-engine like syntax.
This crate is get some inspiration from proc-quote.
This crate is useful for developing proc-macro. Usually an proc-macro crate using template_quote is placed with following Cargo.toml
:
```Cargo.toml [package] name = "yourcratename" version = "0.0.0" edition = "2021"
[lib] proc-macro = true
[dependencies] template-quote = "0.2" proc-macro2 = "1.0" ```
and with following src/lib.rs
code:
```lib.rs extern crate procmacro; extern crate procmacro2; extern crate template_quote;
use templatequote::quote; use procmacro::TokenStream; use proc_macro2::TokenStream as TokenStream2;
pub fn mymacro(: TokenStream) -> TokenStream { quote! { /* something here */ }.into() } ```
then you will be able to use it like:
```rust extern crate yourcratename; use yourcratename::my_macro;
my_macro!() ```
Spacing::Join
, then the emitting punct also has same spacing, whether the '#' token is processed by the macro or not.