diff --git a/src/lib.rs b/src/lib.rs index 4bc4188..81883e1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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. //! @@ -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)*) => { @@ -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)*); diff --git a/tests/test.rs b/tests/test.rs index 748d018..fecb14b 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -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 =);