簡體   English   中英

如何在程序宏中獲取 Literal 的值和類型?

[英]How do I get the value and type of a Literal in a procedural macro?

我正在實現一個類似函數的過程宏,它將單個字符串文字作為參數,但我不知道如何獲取字符串文字的值。

如果我打印變量,它會顯示一堆字段,其中包括類型和值。 它們顯然就在那里,某處。 我如何得到它們?

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 } }"
}

如果您正在編寫過程宏,我建議您考慮使用 crates syn (用於解析)和quote (用於代碼生成)而不是直接使用proc-macro ,因為它們通常更容易處理。

在這種情況下,您可以使用syn::parse_macro_input將令牌 stream 解析為 Rust 的任何語法元素(例如文字、表達式、函數),並且還會在解析失敗時處理錯誤消息。

如果這正是您所需要的,您可以使用LitStr來表示字符串文字。 .value() function 將為您提供一個包含該文字內容的String

您可以使用quote::quote生成宏的output,並使用#將變量的內容插入到生成的代碼中。

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()
}

無數次遇到同樣的問題之后,我終於寫了一個庫來幫助解決這個問題: litrs on crates.io 它的編譯速度比syn更快,並且可以讓您檢查您的文字。

use std::convert::TryFrom;
use litrs::StringLit;
use proc_macro::TokenStream;
use quote::quote;


#[proc_macro]
pub fn my_macro(input: TokenStream) -> TokenStream {
    let input = input.into_iter().collect::<Vec<_>>();
    if input.len() != 1 {
        let msg = format!("expected exactly one input token, got {}", input.len());
        return quote! { compile_error!(#msg) }.into();
    }

    let string_lit = match StringLit::try_from(&input[0]) {
        // Error if the token is not a string literal
        Err(e) => return e.to_compile_error(),
        Ok(lit) => lit,
    };

    // `StringLit::value` returns the actual string value represented by the
    // literal. Quotes are removed and escape sequences replaced with the
    // corresponding value.
    let v = string_lit.value();

    // TODO: implement your logic here
}

有關更多信息,請參閱litrs的文檔


為了獲取有關文字的更多信息, litrs使用LiteralDisplay impl 來獲取字符串表示形式(因為它將在源代碼中編寫),然后解析該字符串。 例如,如果字符串以0x ,則知道它必須是 integer 文字,如果以r#"開頭,則知道它是原始字符串文字。 crate syn的作用完全相同。

當然,鑒於 rustc已經解析了文字,編寫並運行第二個解析器似乎有點浪費。 是的,這很不幸,在 proc_literal 中有一個更好的proc_literal會更好。 但是現在,我認為litrs (或者syn ,如果你正在使用syn )是最好的解決方案。


(PS:我通常不喜歡在 Stack Overflow 上推廣自己的庫,但我對 OP 遇到的問題非常熟悉,我非常認為litrs是目前完成這項工作的最佳工具。)

我一直想要一個字符串文字,所以我發現這個解決方案已經足夠好了。 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()
}

我在解析doc屬性時遇到了類似的問題。 它也表示為TokenStream 這不是確切的答案,但可能會引導正確的方向:

fn from(value: &Vec<Attribute>) -> Vec<String> {
    let mut lines = Vec::new();
    for attr in value {
        if !attr.path.is_ident("doc") {
            continue;
        }
        if let Ok(Meta::NameValue(nv)) = attr.parse_meta() {
            if let Lit::Str(lit) = nv.lit {
                lines.push(lit.value());
            }
        }
    }
    lines
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM