Почему я получаю «неожиданный токен» при попытке вызвать внутренний макрос с аргументом внешнего макроса? - PullRequest
0 голосов
/ 20 июня 2019

Я не понимаю эту ошибку при попытке передать выражение, полученное макросом higher!, в макрос lower!:

// A low-level macro using only Rust primitives.
macro_rules! lower {
    (x, $a:expr) => {
        println!("x is {}", $a);
    };
    (x($b:expr), $a:expr) => {
        println!("x({}) is rather {}", $b, $a);
    };
}

// A higher-level macro using my own previous macro.
macro_rules! higher {
    ($xstuff:expr, $a:expr) => {
        // Here, I expect transferring the expression $xstuff to lower!.. but it fails.
        lower!($xstuff, $a)
    };
}

fn main() {
    lower!(x, '5'); // x is 5
    lower!(x(8), '6'); // x(8) is rather 6

    higher!(x(7), '9'); 
}
error: no rules expected the token `x(7)`
  --> src/main.rs:15:16
   |
2  | macro_rules! lower {
   | ------------------ when calling this macro
...
15 |         lower!($xstuff, $a)
   |                ^^^^^^^ no rules expected this token in macro call
...
23 |     higher!(x(7), '9'); 
   |     ------------------- in this macro invocation

Я бы ожидал, что последний маркер будет ожидаться правилом в lower!, но компилятор говорит мне, что это неожиданно. Что мне здесь не хватает? Как я могу передать выражение, полученное higher! как $xstuff, на lower!?

1 Ответ

1 голос
/ 20 июня 2019

После вызова higher!, x(7) был проанализирован как полное выражение , содержащееся в макропеременной $xstuff:

    ($xstuff:expr, $a:expr) => { /* ... */ }
//           ^~~~

Однако ни одно из правил макроса для lower! не принимает произвольное выражение в качестве первого аргумента, они принимают только маркер x:

    (x, $a:expr) => { /* ... */ }
//   ^
    (x($b:expr), $a:expr) => { /* ... */ }
//   ^

Самое простое решение - поместить те же ограничения, что и x, в более высокий макрос:

macro_rules! higher {
    (x($xstuff:expr), $a:expr) => {
        lower!(x($xstuff), $a)
    };
}

Альтернативное решение (которое изменяет синтаксис вызова) состоит в том, чтобы не сразу анализировать x(7) как выражение, а вместо этого - коллекцию деревьев токенов. Вам нужно добавить дополнительную группировку на сайте вызовов, чтобы парсер знал, когда остановиться, хотя:

macro_rules! higher {
    (($($xstuff:tt)*), $a:expr) => {
        lower!($($xstuff)*, $a)
    };
}

fn main() {
    higher!((x(7)), '9');
}

Смотри также:

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...