通过 Rust 宏自定义文字?

MrM*_*ter 7 rust rust-macros

是否可以在 Rust 中定义一个可以解析自定义文字的宏,例如

vector!(3x + 15y)
Run Code Online (Sandbox Code Playgroud)

澄清一下,我希望能够尽可能接近上述语法(当然在可能的范围内)。

Frx*_*rem 7

我将假设“自定义文字”是指“常规 Rust 文字(不包括原始文字),后面紧跟自定义标识符”。这包括:

  • "str"x,"str"带有自定义后缀的字符串文字x
  • 123x,123带有自定义后缀的数字文字x
  • b"bytes"x,b"bytes"带有自定义后缀的字节文字x

如果以上定义对您来说已经足够了,那么您很幸运,因为根据Rust 参考资料,以上确实是 Rust 中所有有效的文字标记:

后缀是紧跟在文字主要部分之后的非原始标识符(没有空格)。

具有任何后缀的任何类型的文字(字符串、整数等)都可以作为标记有效,并且可以传递给宏而不会产生错误。宏本身将决定如何解释这样的标记以及是否产生错误。

但是,解析为 Rust 代码的文字标记的后缀受到限制。非数字文字标记上的任何后缀都将被拒绝,并且数字文字标记仅接受以下列表中的后缀。

所以 Rust明确 允许宏支持自定义字符串文字。

现在,您将如何编写这样的宏?您不能使用 编写声明性宏macro_rules!,因为无法通过简单的模式匹配来检测和操作自定义文字后缀。但是,可以编写执行此操作的过程宏

我不会详细介绍如何编写程序宏,因为在单个 StackOverflow 答案中写太多。但是,作为起点,我将向您提供这个程序宏示例,它按照您的要求执行某些操作。它接受任何自定义整数文字123x123y在给定的表达式中,并将它们转换为函数调用x_literal(123)y_literal(123)而不是:

extern crate proc_macro;

use proc_macro::TokenStream;
use quote::ToTokens;
use syn::{
    parse_macro_input, parse_quote,
    visit_mut::{self, VisitMut},
    Expr, ExprLit, Lit, LitInt,
};


// actual procedural macro
#[proc_macro]
pub fn vector(input: TokenStream) -> TokenStream {
    let mut input = parse_macro_input!(input as Expr);
    LiteralReplacer.visit_expr_mut(&mut input);
    input.into_token_stream().into()
}

// "visitor" that visits every node in the syntax tree
// we add our own behavior to replace custom literals with proper Rust code
struct LiteralReplacer;

impl VisitMut for LiteralReplacer {
    fn visit_expr_mut(&mut self, i: &mut Expr) {
        if let Expr::Lit(ExprLit { lit, .. }) = i {
            match lit {
                Lit::Int(lit) => {
                    // get literal suffix
                    let suffix = lit.suffix();
                    // get literal without suffix
                    let lit_nosuffix = LitInt::new(lit.base10_digits(), lit.span());

                    match suffix {
                        // replace literal expression with new expression
                        "x" => *i = parse_quote! { x_literal(#lit_nosuffix) },
                        "y" => *i = parse_quote! { y_literal(#lit_nosuffix) },
                        _ => (), // other literal suffix we won't modify
                    }
                }

                _ => (), // other literal type we won't modify
            }
        } else {
            // not a literal, use default visitor method
            visit_mut::visit_expr_mut(self, i)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

例如,宏将转换vector!(3x + 4y)x_literal(3) + y_literal(4).