grit_util/ast_node_traversal.rs
1// Based on: https://github.com/skmendez/tree-sitter-traversal/blob/main/src/lib.rs
2// License: MIT License
3//
4// Copyright (c) 2021 Sebastian Mendez
5
6//!
7//! Iterators to traverse trees with a [`Cursor`] trait to allow for traversing
8//! arbitrary n-ary trees.
9//!
10
11use crate::ast_node::AstNode;
12use core::iter::FusedIterator;
13
14/// Trait which represents a stateful cursor in a n-ary tree.
15/// The cursor can be moved between nodes in the tree by the given methods,
16/// and the node which the cursor is currently pointing at can be read as well.
17pub trait AstCursor {
18 /// The type of the nodes which the cursor points at; the cursor is always pointing
19 /// at exactly one of this type.
20 type Node: AstNode;
21
22 /// Move this cursor to the first child of its current node.
23 ///
24 /// This returns `true` if the cursor successfully moved, and returns `false`
25 /// if there were no children.
26 fn goto_first_child(&mut self) -> bool;
27
28 /// Move this cursor to the parent of its current node.
29 ///
30 /// This returns `true` if the cursor successfully moved, and returns `false`
31 /// if there was no parent node (the cursor was already on the root node).
32 fn goto_parent(&mut self) -> bool;
33
34 /// Move this cursor to the next sibling of its current node.
35 ///
36 /// This returns `true` if the cursor successfully moved, and returns `false`
37 /// if there was no next sibling node.
38 fn goto_next_sibling(&mut self) -> bool;
39
40 /// Get the node which the cursor is currently pointing at.
41 fn node(&self) -> Self::Node;
42}
43
44/// Order to iterate through a n-ary tree; for n-ary trees only
45/// Pre-order and Post-order make sense.
46#[derive(Eq, PartialEq, Hash, Debug, Copy, Clone)]
47pub enum Order {
48 Pre,
49 Post,
50}
51
52/// Iterative traversal of the tree; serves as a reference for both
53/// PreorderTraversal and PostorderTraversal, as they both will call the exact same
54/// cursor methods in the exact same order as this function for a given tree; the order
55/// is also the same as traverse_recursive.
56#[allow(dead_code)]
57fn traverse_iterative<C: AstCursor, F>(mut c: C, order: Order, mut cb: F)
58where
59 F: FnMut(C::Node),
60{
61 loop {
62 // This is the first time we've encountered the node, so we'll call if preorder
63 if order == Order::Pre {
64 cb(c.node());
65 }
66
67 // Keep travelling down the tree as far as we can
68 if c.goto_first_child() {
69 continue;
70 }
71
72 let node = c.node();
73
74 // If we can't travel any further down, try going to next sibling and repeating
75 if c.goto_next_sibling() {
76 // If we succeed in going to the previous nodes sibling,
77 // we won't be encountering that node again, so we'll call if postorder
78 if order == Order::Post {
79 cb(node);
80 }
81 continue;
82 }
83
84 // Otherwise, we must travel back up; we'll loop until we reach the root or can
85 // go to the next sibling of a node again.
86 loop {
87 // Since we're retracing back up the tree, this is the last time we'll encounter
88 // this node, so we'll call if postorder
89 if order == Order::Post {
90 cb(c.node());
91 }
92 if !c.goto_parent() {
93 // We have arrived back at the root, so we are done.
94 return;
95 }
96
97 let node = c.node();
98
99 if c.goto_next_sibling() {
100 // If we succeed in going to the previous node's sibling,
101 // we will go back to travelling down that sibling's tree, and we also
102 // won't be encountering the previous node again, so we'll call if postorder
103 if order == Order::Post {
104 cb(node);
105 }
106 break;
107 }
108 }
109 }
110}
111
112/// Idiomatic recursive traversal of the tree; this version is easier to understand
113/// conceptually, but the recursion is actually unnecessary and can cause stack overflow.
114#[allow(dead_code)]
115fn traverse_recursive<C: AstCursor, F>(mut c: C, order: Order, mut cb: F)
116where
117 F: FnMut(C::Node),
118{
119 traverse_helper(&mut c, order, &mut cb);
120}
121
122fn traverse_helper<C: AstCursor, F>(c: &mut C, order: Order, cb: &mut F)
123where
124 F: FnMut(C::Node),
125{
126 // If preorder, call the callback when we first touch the node
127 if order == Order::Pre {
128 cb(c.node());
129 }
130 if c.goto_first_child() {
131 // If there is a child, recursively call on
132 // that child and all its siblings
133 loop {
134 traverse_helper(c, order, cb);
135 if !c.goto_next_sibling() {
136 break;
137 }
138 }
139 // Make sure to reset back to the original node;
140 // this must always return true, as we only get here if we go to a child
141 // of the original node.
142 assert!(c.goto_parent());
143 }
144 // If preorder, call the callback after the recursive calls on child nodes
145 if order == Order::Post {
146 cb(c.node());
147 }
148}
149
150struct PreorderTraverse<C> {
151 cursor: Option<C>,
152}
153
154impl<C> PreorderTraverse<C> {
155 pub fn new(c: C) -> Self {
156 PreorderTraverse { cursor: Some(c) }
157 }
158}
159
160impl<C> Iterator for PreorderTraverse<C>
161where
162 C: AstCursor,
163{
164 type Item = C::Node;
165
166 fn next(&mut self) -> Option<Self::Item> {
167 let c = match self.cursor.as_mut() {
168 None => {
169 return None;
170 }
171 Some(c) => c,
172 };
173
174 // We will always return the node we were on at the start;
175 // the node we traverse to will either be returned on the next iteration,
176 // or will be back to the root node, at which point we'll clear out
177 // the reference to the cursor
178 let node = c.node();
179
180 // First, try to go to a child or a sibling; if either succeed, this will be the
181 // first time we touch that node, so it'll be the next starting node
182 if c.goto_first_child() || c.goto_next_sibling() {
183 return Some(node);
184 }
185
186 loop {
187 // If we can't go to the parent, then that means we've reached the root, and our
188 // iterator will be done in the next iteration
189 if !c.goto_parent() {
190 self.cursor = None;
191 break;
192 }
193
194 // If we get to a sibling, then this will be the first time we touch that node,
195 // so it'll be the next starting node
196 if c.goto_next_sibling() {
197 break;
198 }
199 }
200
201 Some(node)
202 }
203}
204
205struct PostorderTraverse<C> {
206 cursor: Option<C>,
207 retracing: bool,
208}
209
210impl<C> PostorderTraverse<C> {
211 pub fn new(c: C) -> Self {
212 PostorderTraverse {
213 cursor: Some(c),
214 retracing: false,
215 }
216 }
217}
218
219impl<C> Iterator for PostorderTraverse<C>
220where
221 C: AstCursor,
222{
223 type Item = C::Node;
224
225 fn next(&mut self) -> Option<Self::Item> {
226 let c = match self.cursor.as_mut() {
227 None => {
228 return None;
229 }
230 Some(c) => c,
231 };
232
233 // For the postorder traversal, we will only return a node when we are travelling back up
234 // the tree structure. Therefore, we go all the way to the leaves of the tree immediately,
235 // and only when we are retracing do we return elements
236 if !self.retracing {
237 while c.goto_first_child() {}
238 }
239
240 // Much like in preorder traversal, we want to return the node we were previously at.
241 // We know this will be the last time we touch this node, as we will either be going
242 // to its next sibling or retracing back up the tree
243 let node = c.node();
244 if c.goto_next_sibling() {
245 // If we successfully go to a sibling of this node, we want to go back down
246 // the tree on the next iteration
247 self.retracing = false;
248 } else {
249 // If we weren't already retracing, we are now; travel upwards until we can
250 // go to the next sibling or reach the root again
251 self.retracing = true;
252 if !c.goto_parent() {
253 // We've reached the root again, and our iteration is done
254 self.cursor = None;
255 }
256 }
257
258 Some(node)
259 }
260}
261
262// Used for visibility purposes, in case this struct becomes public
263struct Traverse<C> {
264 inner: TraverseInner<C>,
265}
266
267enum TraverseInner<C> {
268 Post(PostorderTraverse<C>),
269 Pre(PreorderTraverse<C>),
270}
271
272impl<C> Traverse<C> {
273 pub fn new(c: C, order: Order) -> Self {
274 let inner = match order {
275 Order::Pre => TraverseInner::Pre(PreorderTraverse::new(c)),
276 Order::Post => TraverseInner::Post(PostorderTraverse::new(c)),
277 };
278 Self { inner }
279 }
280}
281
282/// Traverse an n-ary tree using `cursor`, returning the nodes of the tree through an iterator
283/// in an order according to `order`.
284///
285/// `cursor` must be at the root of the tree
286/// (i.e. `cursor.goto_parent()` must return false)
287pub fn traverse<C: AstCursor>(mut cursor: C, order: Order) -> impl FusedIterator<Item = C::Node> {
288 assert!(!cursor.goto_parent());
289 Traverse::new(cursor, order)
290}
291
292impl<C> Iterator for Traverse<C>
293where
294 C: AstCursor,
295{
296 type Item = C::Node;
297
298 fn next(&mut self) -> Option<Self::Item> {
299 match self.inner {
300 TraverseInner::Post(ref mut i) => i.next(),
301 TraverseInner::Pre(ref mut i) => i.next(),
302 }
303 }
304}
305
306// We know that PreorderTraverse and PostorderTraverse are fused due to their implementation,
307// so we can add this bound for free.
308impl<C> FusedIterator for Traverse<C> where C: AstCursor {}