3 个不稳定版本

0.2.1 2022年7月20日
0.2.0 2022年7月18日
0.1.1 2022年7月13日
0.1.0 2022年7月13日

Rust 模式 中排名第 2119

MIT 许可证

8KB

visita  

在 Rust 中优雅实现 访问者模式


用法

use visita::*;

pub enum Operation { Add, Sub, Mul, Div }

// Use the `node_group` macro to annotate your group of nodes
// the `data` field allows you to attach additional data to your nodes
[node_group(data = ())]
pub enum Expr {

	NumLit(f64),

	Binary {
		op: Operation,
		lhs: Expr,
		rhs: Expr,
	}

}

// use the `visitor` macro to annotate your visitor structs
// the `output` field marks the result of the visit operation
// this macro will require that your Visitor implements Visit for every variant in the enum
#[visitor(Expr, output = f32)]
struct Interpreter;

impl Visit<NumLit> for Interpreter {
	fn visit(&mut self, node: &NumLit, _data: &Data<Self, NumLit>) -> Self::Output {
		node.0
	}
}

impl Visit<Binary> for Interpreter {
	fn visit(&mut self, node: &Binary_data: &Data<Self, Binary>) -> Self::Output {
		match node.op {
			Operation::Add => node.lhs.accept(self) + node.rhs.accept(self),
			Operation::Sub => node.lhs.accept(self) - node.rhs.accept(self),
			Operation::Mul => node.lhs.accept(self) * node.rhs.accept(self),
			Operation::Div => node.lhs.accept(self) / node.rhs.accept(self),
		}
	}
}

说明

模式的实现分布在 4 个特质之间

// Marks a type as a family of nodes
// and is responsible for routing the visitor to the appropriate visitor methods
pub trait NodeFamily<V> : Sized where V : Visitor<Self> {
	// The additional data we want to tag with the nodes
	type Data;
	// The method responsible for the routing
	fn accept(&self, v: &mut V) -> V::Output;
}

// Marks a type as being a node belonging to a family
pub trait Node<V> : Sized where V : Visitor<Self::Family> + Visit<Self> {
	type Family : NodeFamily<V>;

	fn accept(&self, v: &mut V, data: &Data<V, Self>) -> V::Output {
		v.visit(self, data)
	}
}

// Marks a type as being a visitor to a family of nodes
pub trait Visitor<F> : Sized where F : NodeFamily<Self> {
	// The output of performing this operation
	type Output;
}

// Implements the actual visiting logic for a specific node
// This is the only trait you'll need to implement manually
pub trait Visit<N> : Visitor<N::Family> where N : Node<Self> {
	fn visit(&mut self, node: &N, data: &Data<Self, N>) -> Self::Output;
}

node_group 将执行以下操作

  • 将枚举变体提取到它们自己的结构体中;
  • 创建一个新的枚举,用于分组这些结构体;
  • 创建一个新的结构体,用于保存节点变体和附加数据;
  • 为该结构体实现 NodeFamily;
  • 为结构体变体实现 Node;
#[node_group(data = ())]
enum Expr {
	NumLit(f32),
	Binary(Expr, Operation, Expr),
}

// Becomes:

struct NumLit(f32);

impl<V> Node<V> for NumLit
where V : Visitor<Expr> + Visit<NumLit> + Visit<Binary> {
	type Family = Expr;
}

impl NumLit {
	pub fn to_node(self, data: ()) -> Expr {
		Expr {
			node: ExprNode::NumLit(self),
			data,
		}
	}
}

struct Binary(Expr, Operation, Expr);

impl<V> Node<V> for Binary
where V : Visitor<Expr> + Visit<NumLit> + Visit<Binary> {
	type Family = Expr;
}

impl Binary {
	pub fn to_node(self, data: ()) -> Expr {
		Expr {
			node: Box::new(ExprNode::NumLit(self)),
			data,
		}
	}
}

enum ExprNode {
	NumLit(NumLit),
	Binary(Binary),
}

struct Expr {
	node: Box<ExprNode>,
	data: (),
}

impl<V> NodeFamily<V> for Expr
where V : Visitor<Expr> + Visit<NumLit> + Visit<Binary> {
	type Data = ();
	fn accept(&self, v: &mut V) -> V::Output {
		match self.node.as_ref() {
			ExprNode::NumLit(node) => v.visit(node, &self.data),
			ExprNode::Binary(node) => v.visit(node, &self.data),
		}
	}
}

要构造一个节点,您可以使用:NumLit(23.0).to_node(())

visitor 宏简单地为类型实现 Visitor 特质

#[visitor(Expr, output = f32)]
struct Interpreter;

// Becomes:

struct Interpreter;

impl Visitor<Expr> for Interpreter {
	type Output = f32;
}

由于 node_group 宏的限制,将类型标记为 visitor 还需要为该节点家族中的每个可能的节点实现 Visit

依赖项

~1.5MB
~35K SLoC