通过Rust宏自定义文字?

bq9c1y66  于 2023-04-06  发布在  其他
关注(0)|答案(1)|浏览(216)

在Rust中有没有可能定义一个宏来解析自定义的文字,例如:

vector!(3x + 15y)

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

tjjdgumg

tjjdgumg1#

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

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

如果上面的定义对你来说足够了,那么你很幸运,因为根据Rust参考,上面的确实是Rust中所有有效的文字标记:
后缀是紧接在文字的主要部分之后的非原始标识符(没有空格)。

任何带后缀的文字(字符串、整数等)都是有效的token,可以传递给宏而不会产生错误,宏自己决定如何解释这样的token,是否产生错误。

但是,解析为Rust代码的文字标记的后缀是受限制的。任何非数字文字标记的后缀都被拒绝,数字文字标记只接受下面列表中的后缀。
所以Rust * 显式地 * 允许宏支持自定义文字。
现在,你将如何编写这样一个宏呢?你不能用macro_rules!编写一个声明性宏,因为它不可能通过简单的模式匹配来检测和操作自定义的文字后缀。但是,可以编写一个procedural macro来做到这一点。
我不会详细介绍如何编写过程宏,因为在一个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)
        }
    }
}

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

相关问题