Как я могу обработать строку, сгенерированную из макроса, внутри процедурного макроса?

Я использую include_str!(), чтобы импортировать строку из файла и передать ее в proc_marco, но это не работает. Я получаю ошибку expected string literal. Вот мой код:

macro_rules! ptcl_layer {
    () => {
        include_str!("tcp_to_msg.layer")
    };
}

pub static TEMPLETE: &'static str = ptcl_layer!();
pub static TEMPLETE_HEADER: &'static str = get_protocol_layer_header!(ptcl_layer!());
pub static TEMPLETE_END: &'static str = get_protocol_layer_end!(ptcl_layer!());
#[proc_macro]
pub fn get_protocol_layer_header(_s: TokenStream) -> TokenStream {
    let total_str = parse_macro_input!(_s as LitStr).value();
    let header = total_str.lines().next().unwrap();
    let header = header.replace("${.LEN}\n", "");
    let output = quote::quote! {
        #header
    };
    TokenStream::from(output)
}

#[proc_macro]
pub fn get_protocol_layer_end(_s: TokenStream) -> TokenStream {
    let input = parse_macro_input!(_s as LitStr);
    let input = input.value();
    let last_line = input.lines().last().unwrap_or("");
    let output = quote::quote! {
        #last_line
    };
    
    TokenStream::from(output)
}

Полное сообщение об ошибке:

error: expected string literal
  --> src/controller/mod.rs:16:71
   |
16 | pub static TEMPLETE_HEADER: &'static str = get_protocol_layer_header!(ptcl_layer!());
   |                                                                       ^^^^^^^^^^

И *.layer вроде:

<? lines: ${.LEN}
${.CONTENT}
<? end

Я также попробовал другую версию кода:

#[proc_macro]
pub fn get_protocol_layer_header(_s: TokenStream) -> TokenStream {
    let total_str = _s.to_string();
    let header = total_str.lines().next().unwrap();
    let header = header.replace("${.LEN}\n", "");
    header.parse().unwrap()
}

#[proc_macro]
pub fn get_protocol_layer_end(_s: TokenStream) -> TokenStream {
    let total_str = _s.to_string();
    let last_line = total_str.lines().last().unwrap_or("");
    last_line.parse().unwrap()
}

Но он вернул исходный текст без какой-либо обработки.

Макросы расширяются снаружи внутрь, поэтому ваш макрос get_protocol_layer_header! вызывается с входными токенами ptcl_layer!(), а затем, если его выходные данные содержат макрос, такой как ptcl_layer!(), то они будут расширены следующими.

eggyal 23.06.2024 09:44
Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
0
1
89
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Как заметил eqqyal, по умолчанию макросы оцениваются снаружи, но когда какое-то выражение приведет к литералу, как в вашем случае, вы можете инвертировать это, используя нестабильный TokenStream::expand_expr:

вверху your_macros/src/lib.rs:

#![feature(proc_macro_expand)]

в определениях, которые содержат LitStr, вставьте следующее:

#[proc_macro]
pub fn get_protocol_layer_header(input: TokenStream) -> TokenStream {
    let input = input.expand_expr().unwrap();
    let total_str = parse_macro_input!(input as LitStr).value();
    // …

Это то, что должны принимать самые внешние макросы, которые вы хотите использовать, поэтому, если вы не можете изменить определение get_protocol_layer_header и ему подобных или не можете использовать каждую ночь, вам придется обойти это и вставить соответствующие строковые литералы с помощью build.rs srcipt. .

Другие вопросы по теме