dtolnay / quote

Rust quasi-quoting
Apache License 2.0
1.32k stars 90 forks source link

Interpolate blocks #276

Closed Pistonight closed 4 months ago

Pistonight commented 4 months ago

This is my design/attempt at implementing #275

Proposed Syntax

I spent a lot of time thinking about what the syntax should be. IMO, it should:

  1. Compatible with existing syntax and design language (i.e. use # to indicate interpolation)
  2. Allow users to easily tell if a block is quoted vs interpolated
  3. Allow users to easily tell where the start and end of that block is
  4. Be intuitive and does not invent a very different syntax
  5. Does not introduce ambiguity or tricky edge cases

I started with #{ ... }, went though many iterations and in the end settled on #@{ ... }@:

Proposed Behavior

There are 2 ways where this could be implemented, with different behaviors:

  1. A separated pass to extract all interpolation block, and execute them before the second pass to process idents and repetitions
  2. Still keep the single pass design, and execute the block where it's at and add the output to the running TokenStream

I chose 2 since the implementation is way simpler, and probably more intuitive when composed with #( ... )*. This implementation also allows the blocks to access the bindings generated by #( ... )*, which becomes useful if we make RepInterp implement Deref. Just a dummy example:

let a = quote! { a };
let b = quote! { b };
let ids2 = vec![ vec![&a, &b], vec![&b, &a] ];

let tokens = quote! {
    #(
        invoke(#(#ids2),*);
        #@{
            let mut t = TokenStream::new();
            t.extend(ids2[0].clone());
            t.extend(quote! { + });
            t.extend(ids2[1].clone());
            t
        }@
    )*
};
// output: invoke (a, b) ; a + b invoke (b, a) ; b + a

Also, for ergonomic reasons, it's ideal for the macro to handle both when the block outputs a ToTokens or an iterator of them. This is implemented with a same trick used for repetition where a to_tokens method shadows the trait method if the trait is not implemented

Other

Please let me know if there are other concerns

Tasks