模仿制作Interpeter的BINARY_OP宏



在尝试遵循优秀的Crafting Interpreters书籍时遇到了这个问题,但使用Rust而不是c。

这本书构建了一个基于堆栈的虚拟机,我有一个简单的Rust版本,看起来像:

struct VM {
stack: Vec<Value>
}
impl VM {
//...
pub fn interpret(op: OpCode) {
match op {
Op::Add => {
let r = self.stack.pop();
let l = self.stack.pop();
self.stack.push(l + r);
}
// Repeated for -, *, and /
}
}
}

这本书使用了一个C风格的宏来防止对所有的binops复制/粘贴相同的代码。

#define BINOP(op) 
do { 
double r = pop(); 
double l = pop(); 
push(l op r); 
} while (false)
void interpret(VM *vm, OpCode op) {
switch op {
case OP_ADD: BINOP(+); break;
case OP_SUB: BINOP(-); break;
case OP_MUL: BINOP(*); break;
case OP_DIV: BINOP(/); break;
}
我尝试使用Rust宏 做类似的事情
macro_rules! binop {
($op:tt) => {
{
let l = self.stack.pop(); 
let r = self.stack.pop(); 
self.stack.push(l $op r) };
}
}
// ... inside VM impl ...
pub fn interpret(&mut self, op: OpCode) {
match op {
Op::Add => binop!(+)
// Repeated for -, *, and /
}
}

但是宏确实知道self是什么,我不确定让宏意识到self的最佳方法是什么。

在Rust中是否有一种方法可以实现类似于C宏的东西?

您可以将self作为宏的参数:

macro_rules! binop {
($self:expr, $op:tt) => {{
let l = $self.stack.pop(); 
let r = $self.stack.pop(); 
$self.stack.push(l $op r);
}};
}
如果在其他地方不需要宏,也可以在函数中定义宏:
pub fn interpret(&mut self, op: OpCode) {
macro_rules! binop {
($op:tt) => {{
let l = self.stack.pop(); 
let r = self.stack.pop(); 
self.stack.push(l $op r);
}};
}
match op {
Op::Add => binop!(+)
// Repeated for -, *, and /
}
}

另一个选择是使用闭包而不是宏:

impl VM {
pub fn interpret(&mut self, op: OpCode) {
match op {
OpCode::Add => self.binop(|l, r| l + r),
OpCode::Sub => self.binop(|l, r| l - r),
OpCode::Mul => self.binop(|l, r| l * r),
OpCode::Div => self.binop(|l, r| l / r),
}
}
pub fn binop(&mut self, f: impl Fn(Value, Value) -> Value) {
// Of course, in a real case you should handle unwrap:
let l = self.stack.pop().unwrap();
let r = self.stack.pop().unwrap();
self.stack.push(f(l, r));
}
}

相关内容

最新更新