dmc_transform/visit.rs
1use dmc_parser::ast::*;
2
3/// Result of visiting one node. Walker consumes this when iterating the
4/// parent's children Vec.
5pub enum NodeAction {
6 /// Walker recurses into this node's children.
7 Keep,
8 /// Node stays in place but its children are not visited.
9 KeepSkipChildren,
10 /// Splice these nodes into the parent's `children` at the current index.
11 /// Replacements are NOT re-visited (prevents infinite loops on
12 /// transformers that produce nodes matching their own pattern).
13 Replace(Vec<Node>),
14 /// Drop this node from the parent's children.
15 Remove,
16}
17
18/// Implemented by transform passes to react to each node. The default impl
19/// keeps every node and recurses, so override only the variants you care about.
20pub trait Visitor {
21 fn visit_node(&mut self, _node: &mut Node) -> NodeAction {
22 NodeAction::Keep
23 }
24}
25
26/// Recurse into the per-variant inner children of `parent`. Leaf variants
27/// (Text, InlineCode, CodeBlock, Image, JsxExpression, ...) are no-ops.
28pub fn walk_children_mut<V: Visitor>(parent: &mut Node, v: &mut V) {
29 if let Node::Table(t) = parent {
30 for row in &mut t.children {
31 for cell in &mut row.cells {
32 walk_root(&mut cell.children, v);
33 }
34 }
35 return;
36 }
37
38 if let Some(kids) = Node::children_of_mut(parent) {
39 walk_root(kids, v);
40 }
41}
42
43/// Drive the visitor over a `Vec<Node>`, honoring every `NodeAction` variant.
44/// Replacements aren't re-visited but are descended into on a later pass if
45/// the visitor returns `Keep`.
46pub fn walk_root<V: Visitor>(children: &mut Vec<Node>, v: &mut V) {
47 let mut i = 0;
48 while i < children.len() {
49 match v.visit_node(&mut children[i]) {
50 NodeAction::Keep => {
51 walk_children_mut(&mut children[i], v);
52 i += 1;
53 },
54 NodeAction::KeepSkipChildren => {
55 i += 1;
56 },
57 NodeAction::Replace(new) => {
58 let n = new.len();
59 children.splice(i..=i, new);
60 i += n;
61 },
62 NodeAction::Remove => {
63 children.remove(i);
64 },
65 }
66 }
67}