是否可以在宏内修改令牌的大小写? [英] Is it possible to modify the case of a token inside of a macro?

查看:8
本文介绍了是否可以在宏内修改令牌的大小写?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在编写一个宏来创建一个管理用户输入的结构.我正在使用板条箱 bitflagssdl2.Return 是键 Return 的示例.

I am writing a macro which creates a struct managing user input. I am using the crates bitflags and sdl2. Return is an example for the key Return.

此宏获取所有可能输入的列表,然后

This macro takes a list of all possible inputs and then

($($flag:ident = $value:expr;)+)  => { ... }

  1. 使用输入的名称创建一个新的位标志

  1. Creates a new bitflag with the name of the input

bitflags!(
    struct KeyType: u64 {
    $(
        const $flag = $value;// UPPER_CASE is the norm for globals: 'RETURN'
    )+
    }
);

  • 检查按键是否被按下,使用 键码枚举.

    match event {
        $(Event::KeyDown { keycode: Some(Keycode::$flag), .. } => { 
            self.flags.insert($flag); 
        },)+
         _ => ()
    }// All enum fields start with a capital letter: 'Return'
    

  • 创建一个getter函数:

  • Creates a getter function:

    $(
        pub fn $flag(&self) -> bool { // lower_case is the norm for functions: 'return'
            self.flags.contains($flag)
        }
    )+
    

  • 我可以调整 $flag 以适应所有三个要求(flagFlagFLAG)吗?

    Can I adapt $flag to fit all three requirements (flag, Flag, FLAG)?

    推荐答案

    Macro_rules 宏不能做到这一点.您需要将其实现为过程宏,允许执行任意 Rust 代码以生成扩展代码.特别是,过程宏能够调用 str::to_uppercasestr::to_lowercase.

    Macro_rules macros cannot do this. You would need to implement it as a procedural macro, which is allowed to perform arbitrary Rust code to generate the expanded code. In particular, procedural macros are able to call str::to_uppercase and str::to_lowercase.

    // [dependencies]
    // quote = "1.0"
    // syn = "1.0"
    
    use proc_macro::TokenStream;
    use quote::quote;
    use syn::parse::{Parse, ParseStream, Result};
    use syn::{parse_macro_input, Expr, Ident, Token};
    
    struct Input {
        flags: Vec<Ident>,
        values: Vec<Expr>,
    }
    
    // $( $flag:ident = $value:expr; )*
    impl Parse for Input {
        fn parse(input: ParseStream) -> Result<Self> {
            let mut flags = Vec::new();
            let mut values = Vec::new();
            while !input.is_empty() {
                flags.push(input.parse()?);
                input.parse::<Token![=]>()?;
                values.push(input.parse()?);
                input.parse::<Token![;]>()?;
            }
            Ok(Input { flags, values })
        }
    }
    
    #[proc_macro]
    pub fn sleeping_panda(input: TokenStream) -> TokenStream {
        let input = parse_macro_input!(input as Input);
    
        let camelcase_flags = &input.flags; // assume CamelCase in the input
        let bitflag_values = &input.values;
    
        let uppercase_flags = input
            .flags
            .iter()
            .map(|ident| Ident::new(&ident.to_string().to_uppercase(), ident.span()));
        // Some copies because these need to appear multiple times in the generated code.
        let uppercase_flags2 = uppercase_flags.clone();
        let uppercase_flags3 = uppercase_flags.clone();
    
        let lowercase_flags = input
            .flags
            .iter()
            .map(|ident| Ident::new(&ident.to_string().to_lowercase(), ident.span()));
    
        TokenStream::from(quote! {
            bitflags::bitflags! (
                struct KeyType: u64 {
                    #(
                        const #uppercase_flags = #bitflag_values;
                    )*
                }
            );
    
            pub struct SleepingPanda {
                flags: KeyType,
            }
    
            impl SleepingPanda {
                pub fn receive_event(&mut self, event: sdl2::event::Event) {
                    match event {
                        #(
                            sdl2::event::Event::KeyDown {
                                keycode: Some(sdl2::keyboard::Keycode::#camelcase_flags),
                                ..
                            } => {
                                self.flags.insert(KeyType::#uppercase_flags2);
                            }
                        )*
                        _ => {}
                    }
                }
    
                #(
                    pub fn #lowercase_flags(&self) -> bool {
                        self.flags.contains(KeyType::#uppercase_flags3)
                    }
                )*
            }
        })
    }
    

    使用宏:

    use sleeping_panda::sleeping_panda;
    
    sleeping_panda! {
        Backspace = 8;
        Tab = 9;
    }
    
    fn main() {}
    

    这篇关于是否可以在宏内修改令牌的大小写?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆