问题描述
我正在编写一个宏来创建一个管理用户输入的结构.我正在使用板条箱 bitflags 和 sdl2.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 .
此宏获取所有可能输入的列表,然后
This macro takes a list of all possible inputs and then
($($flag:ident = $value:expr;)+) => { ... }
使用输入的名称创建一个新的位标志
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
以适应所有三个要求(flag
、Flag
、FLAG
)吗?
Can I adapt $flag
to fit all three requirements (flag
, Flag
, FLAG
)?
推荐答案
Macro_rules 宏不能做到这一点.您需要将其实现为过程宏,允许执行任意 Rust 代码以生成扩展代码.特别是,过程宏能够调用 str::to_uppercase
和 str::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() {}
这篇关于是否可以在宏内修改令牌的大小写?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!