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 {}