Hello! I was thinking a bit about macros, and I had an idea for a kind of syntactic sugar that might be useful to have. I also posted this to the issue tracker at https://github.com/mozilla/rust/issues/9894.

# Current situation

Right now, rust knows about two kinds of macro invocation:

    IDENT!(...)
    IDENT! IDENT (...)

The latter one is just used by `macro_rules!` right now, and seems kinda out of place because of that.

Additionally, just being restricted to `IDENT!(...)` means that, while you can define macros just fine, the resulting invocation syntax often looks a bit weird because of the need for the outer `()` pair.

For example, if you want to write some kind of custom `match` macro you ideally want a syntax like `macro! EXPR { CASES... }`, but in practice are forced to decide between redundant, deeply nested brackets or weird syntax if you want to reduce the brackets:

~~~
my_match!(
    foo().bar().baz() {
        case 1 => ...
        case 2 => ...
        ...
    }
)

my_match!(foo().bar().baz() cases:
    case 1 => ...
    case 2 => ...
    ...
)
~~~

# Proposal

We can't just allow macros to accept different syntax like `IDENT! EXPR ( ... )`, because it would create ambiguity in the parser, but it occurred to me that we _can_ provide syntactic sugar for transforming 'nicer looking' variants into the regular `IDENT!(...)` syntax.

Basically, I'm thinking of leveraging the bang in a macro invocation to annotate how many following bracket pairs to group into one regular macro invocation:

~~~
IDENT!! (...) (...)         => desugaring => IDENT!((...) (...))
IDENT!!! (...) (...) (...)  => desugaring => IDENT!((...) (...) (...))
... etc
~~~

The number of bangs could become confusing fast, but I don't expect that macros with more than two bracket groups are going to be common. And because it would just be sugar, you could always write it as the regular form.

# Advantages

There are a number of advantages I see with this proposal:

1. The two macro invocation forms can be folded into one:
   ~~~
   IDENT!(...)        => IDENT!(...)
   IDENT! IDENT (...) => IDENT!! (IDENT) (...) == IDENT!((IDENT) (...))
   ~~~

2. Custom syntax can become nicer looking, especially for control structures.
   Looking at the `my_match` example:
   ~~~
   my_match!! (foo().bar().baz()) {
       case 1 => ...
       case 2 => ...
       ...
   }
   ~~~
   ... which looks more natural than any the two options outlined above.

3. It's pure syntactic sugar, which means it's easy to implement and reason about. All `libsyntax` needs to do is to consume a list of bracket-counted token trees equal to the number of bangs, and introduce an artificial outer bracket pair if
   the number is higher than one.

4. It's just invocation sugar, which means there is no difference between defining a macro that uses this vs one that doesn't - you just declare them all assuming the
   explicit outer `()` pair.

# Potential issues

The possible issues I can see with this are confusing error messages if the number of bangs is wrong, and uncertainty about which brackets belong to a macro invocation and which are regular rust code if their contents become big and/or their number become high.

However, in theory rust might be able to provide good error messages for the first one, because if the macro name is right you'd get an `No rules expected this ...` error, and there could be some heuristics for recognizing cases where the user has do add/remove bangs to match the macro definition.

And the second one can likely be managed by syntax highlighting, and by the usual convention of not abusing the syntax to the point where it becomes illegible.

# Backwards compatibility

Adding this sugar would be backwards compatible because it doesn't change any existing macro syntax, however if `IDENT! IDENT (...)` ends up obsolete through this, that would break existing uses of `macro_rules!` - but macros are behind a feature flag anyway at this point, so it wouldn't matter as much.

_______________________________________________
Rust-dev mailing list
[email protected]
https://mail.mozilla.org/listinfo/rust-dev

Reply via email to