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