As I understand it, there is a high incentive to have macros be tokenizable and parseable without needing to expand them - it's a necessarily if we ever want importable macros (Can't resolve macro imports if you can't parse the source). Hence the requirement to have explicit brackets and the need to have properly nested and paired brackets in them.

On 10/17/2013 06:30 AM, Oren Ben-Kiki wrote:
In general I'd favor anything that would help with https://github.com/mozilla/rust/issues/9358 - that is, allow more eDSL-ish macros. Writing `foo!!(...) { ... }` has the cost of the extra `!` but that really isn't too bad.

That said, I wonder why do macro invocations require the outer set of parenthesis? Since a macro is expressed in terms of parser rules (expressions, identifiers, tokens, etc.), isn't it unambiguous to allow macros to be invoked without the outer `()`? E.g.:

macro_rules! foo (
    (bar: $expr :<: baz: $expr) => ...,
)

Today, one would use it as in `f(foo!(1 :<: 2), 3)`. But suppose one could somehow indicate the outer `()` were never to be used for this macro (say writing `syntax_rules!` instead of `macro_rules!`, or by some other extension of the `macro_rules!` notation), then parsing `f(foo! 1 :<: 2, 3)` would be unambiguous - assuming we (1) strictly use greedy parsing of $expr etc. and (2) always expand a later macro before parsing and expanding earlier macros, where later and earlier are in source text order. This strategy makes irrelevant the issue of determining the nesting of such macros while "doing the right thing" with a simple deterministic rule.

This would probably be more complex to implement than the multiple-`!` proposal, but would allow for more generic patterns. For example, allowing to omit the outer `()` would allow invoking `my_match! pattern => action`; if I understand it correctly, using `!!` would only allow for writing `my_match!! pattern action`.

At any rate, this is just as an idea - like I said, I'd like anything that would allow me to write `foo!(...) { ... }` (or `foo!!(...) { ... }`, or something along these lines). Writing `foo!(..., { ... })` just seems ugly to me...


On Thu, Oct 17, 2013 at 2:10 AM, Marvin Löbel <[email protected] <mailto:[email protected]>> wrote:

    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] <mailto:[email protected]>
    https://mail.mozilla.org/listinfo/rust-dev



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

Reply via email to