Как получить значение и тип литерала в макросе rust pro c? - PullRequest
2 голосов
/ 12 апреля 2020

Я пытаюсь реализовать функциональный процедурный макрос, который принимает в качестве аргумента однострочный литерал. Я не знаю, как получить значение строкового литерала. Если я распечатаю переменную, она показывает кучу полей, которые включают как тип, так и значение. Они явно там, где-то. Как мне их получить?

extern crate proc_macro;
use proc_macro::{TokenStream,TokenTree};

#[proc_macro]
pub fn my_macro(input: TokenStream) -> TokenStream {
    let input: Vec<TokenTree> = input.into_iter().collect();
    let literal = match &input.get(0) {
        Some(TokenTree::Literal(literal)) => literal,
        _ => panic!()
    };

    // can't do anything with "literal"
    // println!("{:?}", literal.lit.symbol); says "unknown field"

    format!("{:?}", format!("{:?}", literal)).parse().unwrap()
}

#![feature(proc_macro_hygiene)]
extern crate macros;

fn main() {
    let value = macros::my_macro!("hahaha");
    println!("it is {}", value);
    // prints "it is Literal { lit: Lit { kind: Str, symbol: "hahaha", suffix: None }, span: Span { lo: BytePos(100), hi: BytePos(108), ctxt: #0 } }"
}

Ответы [ 2 ]

1 голос
/ 12 апреля 2020

Если вы пишете процедурные макросы, я бы порекомендовал вам использовать ящики syn (для разбора) и quote (для генерации кода) вместо использования proc-macro напрямую, поскольку с ними, как правило, легче иметь дело.

В этом случае вы можете использовать syn::parse_macro_input для анализа потока токенов в любом синтаксисе c элемент Rust (например, литералы, выражения, функции), а также будет заботиться о сообщениях об ошибках в случае сбоя синтаксического анализа.

Вы можете использовать LitStr для представления строкового литерала, если это именно то, что вам нужно. Функция .value() даст вам String с содержимым этого литерала.

Вы можете использовать quote::quote для генерации вывода макроса и использовать # вставить содержимое переменной в сгенерированный код.

use proc_macro::TokenStream;
use syn::{parse_macro_input, LitStr};
use quote::quote;

#[proc_macro]
pub fn my_macro(input: TokenStream) -> TokenStream {
    // macro input must be `LitStr`, which is a string literal.
    // if not, a relevant error message will be generated.
    let input = parse_macro_input!(input as LitStr);

    // get value of the string literal.
    let str_value = input.value();

    // do something with value...
    let str_value = str_value.to_uppercase();

    // generate code, include `str_value` variable (automatically encodes
    // `String` as a string literal in the generated code)
    (quote!{
        #str_value
    }).into()
}
0 голосов
/ 12 апреля 2020

Я всегда хочу строковый литерал, поэтому я нашел это решение достаточно хорошим. Literal реализует ToString, который затем можно использовать с .parse().

#[proc_macro]
pub fn my_macro(input: TokenStream) -> TokenStream {
    let input: Vec<TokenTree> = input.into_iter().collect();
    let value = match &input.get(0) {
        Some(TokenTree::Literal(literal)) => literal.to_string(),
        _ => panic!()
    };
    let str_value: String = value.parse().unwrap();

    // do whatever

    format!("{:?}", str_value).parse().unwrap()
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...