Macros 如何使用rust中的宏生成复杂的枚举变量
我正在编写一个用于解析OVPN配置文件的小库。OVPN配置文件具有此格式Macros 如何使用rust中的宏生成复杂的枚举变量,macros,rust,Macros,Rust,我正在编写一个用于解析OVPN配置文件的小库。OVPN配置文件具有此格式 command arg1 arg2 othercommand arg1 arg2 有一组固定的命令,其中一些命令有可选参数。我想将解析后的命令表示为枚举。因此,上述情况可能最终表现为: enum ConfigDirective{ Command{arg1: String}, OtherCommand{arg1: String, optinal_arg1: Option<String>}, }
command arg1 arg2
othercommand arg1 arg2
有一组固定的命令,其中一些命令有可选参数。我想将解析后的命令表示为枚举。因此,上述情况可能最终表现为:
enum ConfigDirective{
Command{arg1: String},
OtherCommand{arg1: String, optinal_arg1: Option<String>},
}
fn parse_line(command: String, args: Vec<String>) -> ConfigDirective {
match command {
"command" => ConfigDirective::Command{arg1: args[0]},
"other_command" => ConfigDirective:OtherCommand{arg1: args[0], optional_arg1: args.get(1),
}
}
到目前为止,我能得到的最接近的结果是:
macro_rules! define_config_directives {
($({
rust_name => $rust_name:ident,
required => [$($required:ident),*],
optional => [$($optional:ident),*]
}),*) => {
#[derive(PartialEq, Eq, Debug)]
pub enum ConfigDirective {
$($rust_name{
$($required: String),*,
$($optional: Option<String>),*,
}),*
}
};
}
macro\u规则!定义配置指令{
($({
rust_name=>$rust_name:ident,
必需=>[$($required:ident),*],
可选=>[$($optional:ident),*]
}),*) => {
#[导出(PartialEq、Eq、Debug)]
发布枚举配置指令{
$($rust_名称{
$($必需:字符串),*,
$($可选:选项),*,
}),*
}
};
}
所以我有几个问题:
parse_line
函数,我需要迭代每个必需的参数,以便编写一些代码将相应的参数从行中拉出,对于可选参数也是如此有人知道有没有办法解决这个问题吗?或者我应该使用python脚本生成代码吗?这是一种有点病态的情况。首先,您希望以不同的方式处理部分输入,这是宏所不擅长的。更糟糕的是,您希望在生成枚举变量的同时执行此操作,而宏也不擅长于生成枚举变量。综上所述,就我所见,只剩下一种方法:完全按下推生成 简短的版本是:将其分解为简单的匹配步骤,每个步骤处理一件事情,并将该事情的输出添加到累加器中(在本例中,
$eout
和$pout
)。输入不足时,将累加器转储到输出中
macro_rules! define_config_directive {
// Start rule.
// Note: `$(,)*` is a trick to eat any number of trailing commas.
( $( {$($cmd:tt)*} ),* $(,)*) => {
// This starts the parse, giving the initial state of the output
// (i.e. empty). Note that the commands come after the semicolon.
define_config_directive! { @parse {}, (args){}; $({$($cmd)*},)* }
};
// Termination rule: no more input.
(
@parse
// $eout will be the body of the enum.
{$($eout:tt)*},
// $pout will be the body of the `parse_line` match.
// We pass `args` explicitly to make sure all stages are using the
// *same* `args` (due to identifier hygiene).
($args:ident){$($pout:tt)*};
// See, nothing here?
) => {
#[derive(PartialEq, Eq, Debug)]
enum ConfigDirective {
$($eout)*
}
fn parse_line(command: &str, $args: &[&str]) -> ConfigDirective {
match command {
$($pout)*
_ => panic!("unknown command: {:?}", command)
}
}
};
// Rule for command with no arguments.
(
@parse {$($eout:tt)*}, ($pargs:ident){$($pout:tt)*};
{
command: $sname:expr,
rust_name: $rname:ident,
args: [],
optional_args: [] $(,)*
},
$($tail:tt)*
) => {
define_config_directive! {
@parse
{
$($eout)*
$rname,
},
($pargs){
$($pout)*
$sname => ConfigDirective::$rname,
};
$($tail)*
}
};
// Rule for other commands.
(
@parse {$($eout:tt)*}, ($pargs:ident){$($pout:tt)*};
{
command: $sname:expr,
rust_name: $rname:ident,
args: [$($args:ident),* $(,)*],
optional_args: [$($oargs:ident),* $(,)*] $(,)*
},
$($tail:tt)*
) => {
define_config_directive! {
@parse
{
$($eout)*
$rname { $( $args: String, )* $( $oargs: Option<String>, )* },
},
($pargs){
$($pout)*
$sname => {
// This trickery is because macros can't count with
// regular integers. We'll just use a mutable index
// instead.
let mut i = 0;
$(let $args = $pargs[i].into(); i += 1;)*
$(let $oargs = $pargs.get(i).map(|&s| s.into()); i += 1;)*
let _ = i; // avoid unused assignment warnings.
ConfigDirective::$rname {
$($args: $args,)*
$($oargs: $oargs,)*
}
},
};
$($tail)*
}
};
}
define_config_directive! {
{command: "command1", rust_name: CommandOne, args: [arg1], optional_args: []},
{command: "other_command", rust_name: OtherCommand, args: [arg1], optional_args: [optional_arg1]},
}
fn main() {
println!("{:?}", parse_line("command1", &["foo"]));
println!("{:?}", parse_line("other_command", &["foo"]));
println!("{:?}", parse_line("other_command", &["foo", "bar"]));
}
macro\u规则!定义配置指令{
//开始规则。
//注意:`$(,)*`是一个可以吃任意数量尾随逗号的技巧。
($({$($cmd:tt)*}),*$(,)*)=>{
//这将启动解析,给出输出的初始状态
//(即空)。请注意,命令位于分号之后。
定义配置指令!{@parse{},(args){};$({$($cmd)*},)*}
};
//终止规则:不再输入。
(
@解析
//$eout将是枚举的主体。
{$($eout:tt)*},
//$pout将是'parse_line'匹配的主体。
//我们显式地传递'args',以确保所有阶段都在使用
//*相同*`args`(由于标识符卫生)。
($args:ident){$($pout:tt)*};
//看,这里什么都没有?
) => {
#[导出(PartialEq、Eq、Debug)]
枚举配置指令{
$($eout)*
}
fn parse_行(命令:&str,$args:&[&str])->ConfigDirective{
匹配命令{
$($撅嘴)*
_=>死机!(“未知命令:{:?}”,命令)
}
}
};
//不带参数的命令的规则。
(
@解析{$($OUT:tt)*},($pargs:ident){$($pout:tt)*};
{
命令:$sname:expr,
rust_name:$rname:ident,
args:[],
可选参数:[]$(,)*
},
$($tail:tt)*
) => {
定义配置指令{
@解析
{
$($eout)*
$rname,
},
($pargs){
$($撅嘴)*
$sname=>ConfigDirective::$rname,
};
$($尾)*
}
};
//其他命令的规则。
(
@解析{$($OUT:tt)*},($pargs:ident){$($pout:tt)*};
{
命令:$sname:expr,
rust_name:$rname:ident,
参数:[$($args:ident),*$(,)*],
可选参数:[$($oargs:ident),*$(,)*]$(,)*
},
$($tail:tt)*
) => {
定义配置指令{
@解析
{
$($eout)*
$rname{$($args:String,)*$($oargs:Option,)*},
},
($pargs){
$($撅嘴)*
$sname=>{
//这种诡计是因为宏不能与
//正则整数。我们只使用可变索引
//相反。
设muti=0;
$(设$args=$pargs[i].into();i+=1;)*
$(让$oargs=$pargs.get(i).map(|&s | s.into());i+=1;)*
让=i;//避免未使用的分配警告。
ConfigDirective::$rname{
$($args:$args,)*
$($桨:$桨,)*
}
},
};
$($尾)*
}
};
}
定义配置指令!{
{command:“command1”,rust_name:CommandOne,args:[arg1],可选的_args:[]},
{command:“other_command”,rust_name:OtherCommand,args:[arg1],optional_args:[optional_arg1]},
}
fn main(){
println!(“{:?}”,解析行(“command1”和[“foo”);
println!(“{:?}”,parse_行(“其他_命令”&[“foo”]);
println!(“{:?}”,parse_行(“其他_命令”和[“foo”,“bar”));
}
不,不能避免累加器,因为宏不能直接扩展到枚举变量。因此,您必须在一个步骤中扩展到整个枚举定义。这是一种有点病态的情况。首先,您希望以不同的方式处理部分输入,这是宏所不擅长的。更糟糕的是,您希望在生成枚举变量的同时执行此操作,而宏也不擅长于生成枚举变量。综上所述,就我所见,只剩下一种方法:完全按下推生成 简短的版本是:将其分解为简单的匹配步骤,其中每个步骤处理一件事,并添加输出
macro_rules! define_config_directive {
// Start rule.
// Note: `$(,)*` is a trick to eat any number of trailing commas.
( $( {$($cmd:tt)*} ),* $(,)*) => {
// This starts the parse, giving the initial state of the output
// (i.e. empty). Note that the commands come after the semicolon.
define_config_directive! { @parse {}, (args){}; $({$($cmd)*},)* }
};
// Termination rule: no more input.
(
@parse
// $eout will be the body of the enum.
{$($eout:tt)*},
// $pout will be the body of the `parse_line` match.
// We pass `args` explicitly to make sure all stages are using the
// *same* `args` (due to identifier hygiene).
($args:ident){$($pout:tt)*};
// See, nothing here?
) => {
#[derive(PartialEq, Eq, Debug)]
enum ConfigDirective {
$($eout)*
}
fn parse_line(command: &str, $args: &[&str]) -> ConfigDirective {
match command {
$($pout)*
_ => panic!("unknown command: {:?}", command)
}
}
};
// Rule for command with no arguments.
(
@parse {$($eout:tt)*}, ($pargs:ident){$($pout:tt)*};
{
command: $sname:expr,
rust_name: $rname:ident,
args: [],
optional_args: [] $(,)*
},
$($tail:tt)*
) => {
define_config_directive! {
@parse
{
$($eout)*
$rname,
},
($pargs){
$($pout)*
$sname => ConfigDirective::$rname,
};
$($tail)*
}
};
// Rule for other commands.
(
@parse {$($eout:tt)*}, ($pargs:ident){$($pout:tt)*};
{
command: $sname:expr,
rust_name: $rname:ident,
args: [$($args:ident),* $(,)*],
optional_args: [$($oargs:ident),* $(,)*] $(,)*
},
$($tail:tt)*
) => {
define_config_directive! {
@parse
{
$($eout)*
$rname { $( $args: String, )* $( $oargs: Option<String>, )* },
},
($pargs){
$($pout)*
$sname => {
// This trickery is because macros can't count with
// regular integers. We'll just use a mutable index
// instead.
let mut i = 0;
$(let $args = $pargs[i].into(); i += 1;)*
$(let $oargs = $pargs.get(i).map(|&s| s.into()); i += 1;)*
let _ = i; // avoid unused assignment warnings.
ConfigDirective::$rname {
$($args: $args,)*
$($oargs: $oargs,)*
}
},
};
$($tail)*
}
};
}
define_config_directive! {
{command: "command1", rust_name: CommandOne, args: [arg1], optional_args: []},
{command: "other_command", rust_name: OtherCommand, args: [arg1], optional_args: [optional_arg1]},
}
fn main() {
println!("{:?}", parse_line("command1", &["foo"]));
println!("{:?}", parse_line("other_command", &["foo"]));
println!("{:?}", parse_line("other_command", &["foo", "bar"]));
}