2016-03-23 1 views
0

Я пытаюсь сделать макрос, который я могу назвать следующим образом:Macro соответствие одно выражение после [выр *], путь [тт *] и идент [тт *] ветви

mactest!(some::Path[1, 2, AnotherName[3, 4]]) 

Какой бы эквивалентно следующему:

make_result(
    "some::Path", 
    1.convert(), 
    2.convert(), 
    make_result(
     "AnotherName", 
     3.convert(), 
     4.convert() 
    ) 
) 

где convert некоторые черты, которые будут реализованы для связки типов. (convert и make_result имеет тот же тип результата).

Это, насколько я пришел:

// Note: u32 is used as an example result type. 
// The real code attempts to create a more complicated object. 

trait Foo { 
    fn convert(&self) -> u32; 
} 

fn make_result(name: &str, data: Vec<u32>) -> u32 { 
    // This example ignores name and makes a meaningless result 
    data.iter().fold(0,|a, &b| a + b) 
} 


#[macro_export] 
macro_rules! mactest { 
    ([ $($inner:expr),* ]) => {{ 
     let mut result = Vec::new(); 
     $(
      // Process each element. 
      result.push(mactest!($inner)); 
     )* 
     result 
    }}; 
    ($name:path [ $($inner:tt),* ]) => { 
     make_result(stringify!($name), mactest!([$($inner),*])) 
    }; 
    ($name:ident [ $($inner:tt),* ]) => { 
     make_result(stringify!($name), mactest!([$($inner),*])) 
    }; 
    // Process single value. This is never matched? 
    ($x:expr) => { 
     $x.convert() 
    }; 
} 

Первый соответствующий филиал макроса должен соответствовать каждому элементу списка либо к path/ident[items] или одиночному элементу .convert филиала в конце , Но окончательная ветка никогда не была достигнута, а ржавчина жаловалась error: expected ident, found '1', когда отдельные элементы входят в макрос, то есть mactest!(1).

Моих рассуждений, как пользователь начинающей ржавчины, что макрос имеет четыре модели: [expr*], path[tt*], ident[tt*] и expr. Когда я передаю что-то вроде 1 в макрос, я не понимаю, почему любой из вышеприведенных шаблонов должен соответствовать/вмешиваться.

Может кто-нибудь объяснить, почему это не работает? Есть ли способ обхода результата?

+0

макрос не работает в обычном случае: https://play.rust-lang.org/?gist=6ce29f2366951025ad4b&version=stable. Он жалуется на «ожидаемый один из'. ','] 'Или оператор, найденный', '" –

ответ

0

правила макросов проверяются, начиная с первого и идя оттуда. Поэтому, если вы хотите, чтобы ваши другие правила не запускались в особых случаях, вам нужно сначала ввести правило для частного случая.

Try it out in the playground

+0

Кроме того, проблема заключается в том, что внешний вызов совпадает с ': expr', что предотвращает« специальный случай », правила из * когда-либо * сопоставив. Это невозможно сделать, кроме как путем инкрементного разбора. –

+0

Что меня смущает, так это то, что в случае 'mactest (1)' я не вижу, как любое из первых трех правил соответствует '1'. Это особые случаи, поэтому они находятся на вершине. '1' - это не' идентификатор ', не 'путь'. Чтобы сузить его, сообщение об ошибке [this] (https://play.rust-lang.org/?gist=dc58b6e911d75f546cb3&version=stable) действительно смущает меня. @DK. Что в этом контексте означает инкрементный синтаксический анализ? – porgarmingduod

+1

@porgarmingduod Кровать для меня, но это (между прочим) объясняется в [TLBoRM] (https://danielkeep.github.io/tlborm/). Короткая версия: парсер 'macro_rules!' * Действительно * тупой, и тот факт, что '1' не является идентификатором *, является * проблемой. Эта конкретная формулировка * невозможна * для работы; см. раздел 2.3.1. –