Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,19 @@
//! return quote! { /* ... */ #t /* ... */ };
//! ```
//!
//! You can use `#{...}` for arbitrary computations inside of quotations - _the result_ will then
//! be spliced into the token stream:
//!
//! - `#{a[0]}` - index arrays
//! - `#{x.foo}` - access fields
//! - `#{format!("{:?}", ::std::time::Instant::now())}` - arbitrary computations and nested macros,
//! which are valid Rust
//!
//! Note:
//! - interpolation inside of `#foo` inside of `#{...}` is disabled by design. `#{#foo}` is
//! illegal, but on the other hand you can `#{quote!(#foo)}` (if you _really want to_).
//! - computations `#{...}` _inside_ of repetitions `#(...)*` are evaluated each time.
//!
//! Call `to_string()` or `as_str()` on a Tokens to get a `String` or `&str` of Rust
//! code.
//!
Expand Down Expand Up @@ -98,8 +111,8 @@ macro_rules! pounded_var_names {
pounded_var_names!($finish ($($found)*) $($inner)* $($rest)*)
};

($finish:ident ($($found:ident)*) # { $($inner:tt)* } $($rest:tt)*) => {
pounded_var_names!($finish ($($found)*) $($inner)* $($rest)*)
($finish:ident ($($found:ident)*) # { $($ignore:tt)* } $($rest:tt)*) => {
pounded_var_names!($finish ($($found)*) $($rest)*)
};

($finish:ident ($($found:ident)*) # $first:ident $($rest:tt)*) => {
Expand Down Expand Up @@ -219,6 +232,11 @@ macro_rules! quote_each_token {
quote_each_token!($tokens $($rest)*);
};

($tokens:ident # { $($inner:tt)* } $($rest:tt)*) => {
$crate::ToTokens::to_tokens(&{ $($inner)* }, &mut $tokens);
quote_each_token!($tokens $($rest)*);
};

($tokens:ident # $first:ident $($rest:tt)*) => {
$crate::ToTokens::to_tokens(&$first, &mut $tokens);
quote_each_token!($tokens $($rest)*);
Expand Down
67 changes: 67 additions & 0 deletions tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,73 @@ fn test_quote_impl() {
assert_eq!(expected, tokens.as_str());
}

#[test]
fn test_simple_computation_in_interpolation() {
let foo: [u32; 3] = [0, 1, 42];
let tokens = quote!(#{foo[2]});

let expected = "42u32";

assert_eq!(expected, tokens.as_str());
}

#[test]
fn test_complex_computation_in_interpolation() {
let tokens = quote!(
let #{
struct Foo { }

impl quote::ToTokens for Foo {
fn to_tokens(&self, tokens: &mut quote::Tokens) {
tokens.append("foo")
}
}

Foo { }
} = #{ quote!("Hello World!") };
);

let expected = "let foo = \"Hello World!\" ;";

assert_eq!(expected, tokens.as_str());
}

#[test]
fn test_simple_computation_in_loop() {
let foo: [&'static str; 3] = ["1", "2", "3"];
let iter = foo.iter();
let tokens = quote!(#( #iter, #{"+"}, )*);

let expected = r#""1" , "+" , "2" , "+" , "3" , "+" ,"#;

assert_eq!(expected, tokens.as_str());
}

#[test]
fn test_complex_computation_in_loop() {
let foo: [&'static str; 3] = ["1", "2", "3"];
let iter = foo.iter();
let tokens = quote!(
#( // loop
#{ // interpolation
"this will be ignored because the outer loop is empty"
}
)*
#( // loop
#iter,
#{ // interpolation
let foo = ["e", "+", "a"];
let iter = foo.iter();
quote!( #(#iter),*, )
}
)*
);

let expected = r#""1" , "e" , "+" , "a" , "2" , "e" , "+" , "a" , "3" , "e" , "+" , "a" ,"#;

assert_eq!(expected, tokens.as_str());
}

#[test]
fn test_append_tokens() {
let mut tokens = quote!(let x =);
Expand Down