如何删除使用' block'从我的宏来使它更有用?

时间:2015-03-02 06:59:41

标签: rust

我已经编写了这个宏来将任意术语扩展到用于解析的令牌列表中,这允许我这样做:

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);
}

2 个答案:

答案 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);
}

playpen link

让我知道哪些部分最有趣,我可以解释一下(在我有一点睡眠之后...... ^ _ ^)。

答案 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(无论如何你会在运行时解释它。)