Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Matching tt is almost useless #9364

Closed
paulstansifer opened this issue Sep 20, 2013 · 8 comments
Closed

Matching tt is almost useless #9364

paulstansifer opened this issue Sep 20, 2013 · 8 comments
Labels
A-macros Area: All kinds of macros (custom derive, macro_rules!, proc macros, ..) A-syntaxext Area: Syntax extensions

Comments

@paulstansifer
Copy link
Contributor

Because of #3232, the macro parser can't match syntax of the form $($x:some_nt)* under any circumstances. However, this is the natural way to deal with token trees, and adding a delimiter of some kind is prohibitively unergonomic.

As a short-term fix, we could special-case tt so that the macro parser doesn't farm it out to the main Rust parser. We could also introduce a ttseq or something to mean "zero or more tts". It's conceivable that we could also try being smarter with lookahead, but I don't think that's going to be fruitful.

@vadimcn
Copy link
Contributor

vadimcn commented Sep 20, 2013

This came up as I was trying to parse Rust syntax using macros:

macro_rules! stmt_list(
    ( while $cond:expr { $($body:tt)+ } ) => ( while $cond { $($body:tt)+ } );
    ( $head:stmt ; ) => ( { $head; } );
    ( $head:stmt ; $($rest:tt)+ ) => ( { $head ; stmt_list!( $($rest)+ ) } );
)

fn main()
{
    trace_macros!(true);

    stmt_list!(
        while x < 100 {
            let y = x + 1;
            x = y;
        }
    )
}
>rustc --pretty expanded parse.rs
stmt_list! { while y < 0 { let y = x + 1 ; x = y ; } }
parse.rs:37:8: 37:9 error: Local ambiguity: multiple parsing options: built-in NTs tt ('body') or 1 other options.
parse.rs:37         }
                    ^

@vadimcn
Copy link
Contributor

vadimcn commented Sep 20, 2013

I suppose the other way to fix this would be to allow passing interpolated stms as macro parameters. I could then write my statement sequence pattern as
( $head:stmt ; $($rest:stmt);+ ) => ( { $head ; stmt_list!( $($rest);+ ) } );

@paulstansifer
Copy link
Contributor Author

@vadimcn Passing interpolated statements is allowed; the problem is that they cannot be broken apart and re-parsed after they have been parsed once.

vadimcn added a commit to vadimcn/rust that referenced this issue Oct 7, 2014
…g options..." error in macros, which often occurs when trying to match parts of Rust syntax.

For example, this matcher: `fn $name:ident( $($param:ident : $pty:ty),* )` would fail when parsing `fn foo()`, because macro parser wouldn't realize that an ident cannot start with `)`.

This resolves rust-lang#5902, and at least partially mitigates rust-lang#9364 and rust-lang#3232.
@steveklabnik steveklabnik added the A-macros Area: All kinds of macros (custom derive, macro_rules!, proc macros, ..) label Jan 20, 2015
@steveklabnik
Copy link
Member

Triage: updated code:

#![feature(trace_macros)]

macro_rules! stmt_list(
    ( while $cond:expr { $($body:tt)+ } ) => ( while $cond { $($body:tt)+ } );
    ( $head:stmt ; ) => ( { $head; } );
    ( $head:stmt ; $($rest:tt)+ ) => ( { $head ; stmt_list!( $($rest)+ ) } );
);

fn main()
{
    trace_macros!(true);

    stmt_list!(
        while x < 100 {
            let y = x + 1;
            x = y;
        }
    )
}

updated error and output:

stmt_list! { while x < 100 { let y = x + 1 ; x = y ; } }
hello.rs:4:64: 4:72 error: unexpected token: `an interpolated tt`
hello.rs:4     ( while $cond:expr { $($body:tt)+ } ) => ( while $cond { $($body:tt)+ } );
                                                                          ^~~~~~~~

@steveklabnik
Copy link
Member

/cc @kmcallister

@kmcallister kmcallister self-assigned this Jan 20, 2015
@kmcallister kmcallister removed their assignment Feb 25, 2015
@kmcallister
Copy link
Contributor

$($x:tt)* definitely does work; it's a common pattern for forwarding arguments between macros. e.g.

macro_rules! foo {
    ($($x:tt)*) => (println!($($x)*));
}

fn main() {
    foo!("Hello, {}!", "world");
}

The example originally given by @vadimcn should not have $body:tt on the right hand side of a =>, but that doesn't change the outcome. I think what @steveklabnik sees now is a completely different bug, however. The parser shouldn't ever try to process an interpolated tt as a token. It should crack it open and process the tokens inside. I'm going to look into this more.

It seems like the issue @paulstansifer originally raised here was fixed at some point, although I'm not sure.

@vadimcn
Copy link
Contributor

vadimcn commented Feb 25, 2015

Yeah, currently, interpolated tokens on the RHS can only be used as input to other macros.

The parsing problem on the LHS (which is what this issue is about) had been mitigated, - now it is at least possible to match $($x:tt)* when it is followed by a "close" delimiter, i.e.}, ) or ]. I think it would be very hard to fix it in the general case because of inherent parsing ambiguities.

@kmcallister
Copy link
Contributor

Okay, cool. In that case I'm closing this as a dupe of #3232.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-macros Area: All kinds of macros (custom derive, macro_rules!, proc macros, ..) A-syntaxext Area: Syntax extensions
Projects
None yet
Development

No branches or pull requests

4 participants