我已经编写了这个宏来将任意术语扩展到用于解析的令牌列表中,这允许我这样做:
let q = query!({T::U32_(&foo.x)} > {T::U32(0)});
// [U32_(100), Greater, U32(0)]
let q = query!({T::U32_(&foo.x)} != {T::U32_(&bar)});
// [U32_(100), NotEqual, U32_(10)]
let q = query!(
({T::U32_(&foo.x)} > {T::U32(0)}) ||
(({T::U32_(&bar)} == {T::U32(1)}) && ({T::U32_(&bar)} <= {T::U32(10)}))
);
// [Left, U32_(100), Greater, U32(0), Right, Or, Left, Left, U32_(10),
// Equal, U32(1), Right, And, Left, U32_(10), LesserEquals, U32(10),
// Right, Right]
但是,我发现语法很烦人;注意每个枚举必须如何坐在自己的{ ... }
块中才能正确扩展到最终的vec。我更喜欢这个:
let q = query!(T::U32_(&foo.x) > T::U32(0));
let q = query!(T::U32_(&foo.x) != T::U32_(&bar));
let q = query!(
(T::U32_(&foo.x) > T::U32(0)) ||
((T::U32_(&bar) == T::U32(1)) && (T::U32_(&bar) <= T::U32(10)))
);
我整天都在阅读宏观示例,试图弄清楚这一点,但我无法弄清楚是否有一个有效的令牌组合可以让我这样做。
帮助!我怎么能这样做?
在游戏围栏上的完整宏代码:http://is.gd/VM71VQ
#![feature(trace_macros)]
#![allow(dead_code)]
#[derive(Debug)]
enum T<'a> {
Left,
Right,
Equal,
NotEqual,
And,
Or,
Greater,
Lesser,
GreaterEquals,
LesserEquals,
U32(u32),
U32_(&'a u32),
}
macro_rules! query(
// Expand an inner block into its tokens
( EXPAND_BLOCK $x:block ) => (vec!( $x ));
// Expand (
( ( ) => (vec!(T::Left));
// Expand )
( ) ) => (vec!( T::Right ));
// Expand &&
( && ) => (vec!( T::And ));
// Expand >
( > ) => (vec!( T::Greater ));
// Expand <
( < ) => (vec!( T::Lesser ));
// Expand >=
( >= ) => (vec!( T::GreaterEquals ));
// Expand >
( <= ) => (vec!( T::LesserEquals ));
// Expand ==
( == ) => (vec!( T::Equal ));
// Expand !=
( != ) => (vec!( T::NotEqual ));
// Expand ||
( || ) => (vec!( T::Or ));
// Expand ( ... ) into Left, query!(...), Right
(( $($ps:tt)* )) => {
{
let mut rtn:Vec<T> = Vec::new();
rtn.push(T::Left);
for item in query!($($ps)*).into_iter() {
rtn.push(item);
}
rtn.push(T::Right);
rtn
}
};
// Expand a syntax tree parent into it's immediate child elements
// ie. { ... } into query!(...)
({ $($ps:tt)* }) => {
query!(EXPAND_BLOCK { $($ps)* })
};
// Top level expansion into a query using syntax tree rules
($($ps:tt)*) => {
{
let mut rtn:Vec<T> = Vec::new();
$(
for item in query!($ps).into_iter() {
rtn.push(item);
}
)*
rtn
}
};
);
struct Expr {
x: u32
}
fn main() {
let foo = Expr { x: 100u32 };
let bar = 10;
// trace_macros!(true);
let q = query!({T::U32_(&foo.x)} > {T::U32(0)});
println!("{:?}", q);
let q = query!({T::U32_(&foo.x)} != {T::U32_(&bar)});
println!("{:?}", q);
let q = query!(
({T::U32_(&foo.x)} > {T::U32(0)}) ||
(({T::U32_(&bar)} == {T::U32(1)}) && ({T::U32_(&bar)} <= {T::U32(10)}))
);
println!("{:?}", q);
}
答案 0 :(得分:1)
这似乎有效:
#![feature(trace_macros)]
#[derive(Debug)]
enum Item {
Value(u8),
LParen,
RParen,
And,
Or,
LessThan,
GreaterThan,
LessThanEqual,
Equal,
}
macro_rules! query0(
( ( $( $v:tt )* ) ) => ({
let mut result = vec![Item::LParen];
$( result.extend(query0!($v)) );*;
result.push(Item::RParen);
result
});
( || ) => ( vec![Item::Or] );
( && ) => ( vec![Item::And] );
( < ) => ( vec![Item::LessThan] );
( > ) => ( vec![Item::GreaterThan] );
( <= ) => ( vec![Item::LessThanEqual] );
( == ) => ( vec![Item::Equal] );
( $v:expr ) => ( vec![Item::Value($v)] );
);
macro_rules! query(
( $( $v:tt )* ) => ({
let mut result = vec![];
$( result.extend(query0!( $v )) );*;
result
});
);
fn main() {
//trace_macros!(true);
let a = query!(5);
println!("{:?}", a);
let b = query!((1 < 3) == 5);
println!("{:?}", b);
let c = query!((42 > 0) ||
((13 == 13) && (12 <= 99)));
println!("{:?}", c);
}
让我知道哪些部分最有趣,我可以解释一下(在我有一点睡眠之后...... ^ _ ^)。
答案 1 :(得分:0)
我想syntax_rules!
是不可能的。您不能使用简单的$($ps:tt)*
语法,因为T::U32_(&foo.x)
之类的结构将被错误地解析。并且你不能在第二部分使用像($x:item $($ps:tt)*)
这样的模式和宏递归,因为syntax_rules!
解析器需要明确性(如the book中所述)。它会强制您在DSL中引入一些分隔符令牌,例如逗号:query!(T::U32_(&foo.x), >, T::U32(0))
,但它显然比您的块语法更加丑陋。
可能有一个选项可以利用compiler plugin,但我更喜欢使用代数数据类型构建eDSL(无论如何你会在运行时解释它。)