是否可以修改宏内部令牌的大小写?

lnc*_*ncr 6 macros rust

我正在编写一个宏,它创建一个管理用户输入的结构。我现在用的包装箱bitflagsSDL2Return是 key 的一个例子Return

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

($($flag:ident = $value:expr;)+)  => { ... }
Run Code Online (Sandbox Code Playgroud)
  1. 使用输入的名称创建一个新的位标志

    bitflags!(
        struct KeyType: u64 {
        $(
            const $flag = $value;// UPPER_CASE is the norm for globals: 'RETURN'
        )+
        }
    );
    
    Run Code Online (Sandbox Code Playgroud)
  2. 使用Keycode枚举检查键是否被按下。

    match event {
        $(Event::KeyDown { keycode: Some(Keycode::$flag), .. } => { 
            self.flags.insert($flag); 
        },)+
         _ => ()
    }// All enum fields start with a capital letter: 'Return'
    
    Run Code Online (Sandbox Code Playgroud)
  3. 创建一个 getter 函数:

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

$flag可以适应所有三个要求 ( flag, Flag, FLAG) 吗?

dto*_*nay 15

paste板条箱有助于在 Macro_rules 宏中进行标识符大小写转换,用于[<$ident:lower>]小写和[<$ident:upper>]大写。

像这样的东西:

// [dependencies]
// paste = "0.1"

macro_rules! sleeping_panda {
    ($($flag:ident = $value:expr;)+) => {
        paste::item! {
            bitflags::bitflags! (
                struct KeyType: u64 {
                    $(
                        const [<$flag:upper>] = $value;
                    )*
                }
            );

            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::$flag),
                                ..
                            } => {
                                self.flags.insert(KeyType::[<$flag:upper>]);
                            }
                        )*
                        _ => {}
                    }
                }

                $(
                    pub fn [<$flag:lower>](&self) -> bool {
                        self.flags.contains(KeyType::[<$flag:upper>])
                    }
                )*
            }
        }
    };
}

sleeping_panda! {
    Backspace = 8;
    Tab = 9;
}
Run Code Online (Sandbox Code Playgroud)


dto*_*nay 5

Macro_rules 宏无法做到这一点。您需要将它实现为一个过程宏,它允许执行任意 Rust 代码以生成扩展代码。特别是,程序宏能够调用str::to_uppercasestr::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)
                }
            )*
        }
    })
}
Run Code Online (Sandbox Code Playgroud)

使用宏:

use sleeping_panda::sleeping_panda;

sleeping_panda! {
    Backspace = 8;
    Tab = 9;
}

fn main() {}
Run Code Online (Sandbox Code Playgroud)