cairo_lang_syntax/node/iter.rs
1use salsa::Database;
2
3use crate::node::SyntaxNode;
4
5/// `WalkEvent` describes tree walking process.
6#[derive(Debug, Copy, Clone)]
7pub enum WalkEvent<T> {
8 /// Fired before traversing the node.
9 Enter(T),
10 /// Fired after the node is traversed.
11 Leave(T),
12}
13
14impl<T> WalkEvent<T> {
15 pub fn map<U>(self, f: impl FnOnce(T) -> U) -> WalkEvent<U> {
16 match self {
17 WalkEvent::Enter(it) => WalkEvent::Enter(f(it)),
18 WalkEvent::Leave(it) => WalkEvent::Leave(f(it)),
19 }
20 }
21}
22
23/// Traverse the subtree rooted at the current node (including the current node) in preorder,
24/// excluding tokens.
25pub struct Preorder<'a> {
26 db: &'a dyn Database,
27 // FIXME(mkaput): Is it possible to avoid allocating iterators in layers here?
28 // This code does it because without fast parent & prev/next sibling operations it has to
29 // maintain DFS trace.
30 layers: Vec<PreorderLayer<'a>>,
31}
32
33struct PreorderLayer<'a> {
34 start: SyntaxNode<'a>,
35 children: Option<(&'a [SyntaxNode<'a>], usize)>,
36}
37
38impl<'a> Preorder<'a> {
39 pub(super) fn new(start: SyntaxNode<'a>, db: &'a dyn Database) -> Self {
40 let initial_layer = PreorderLayer { start, children: None };
41
42 // NOTE(mkaput): Reserving some capacity should help amortization and thus make this
43 // iterator more performant. This wasn't benchmarked though and the capacity is just an
44 // educated guess, based on typical depth of syntax files in test suites.
45 let mut layers = Vec::with_capacity(32);
46 layers.push(initial_layer);
47
48 Self { db, layers }
49 }
50}
51
52impl<'a> Iterator for Preorder<'a> {
53 type Item = WalkEvent<SyntaxNode<'a>>;
54
55 fn next(&mut self) -> Option<Self::Item> {
56 // Lack of layers to traverse means end of iteration, so early return here.
57 //
58 // The layer is popped here to gain exclusive ownership of it without taking exclusive
59 // ownership of the layers stack.
60 let mut layer = self.layers.pop()?;
61 match layer.children.take() {
62 None => {
63 // #1: If children iterator is not initialized, this means entire iteration just
64 // started, and the enter event for start node has to be emitted.
65 let event = WalkEvent::Enter(layer.start);
66 layer.children = Some((layer.start.get_children(self.db), 0));
67 self.layers.push(layer);
68 Some(event)
69 }
70 Some((nodes, index)) => {
71 match nodes.get(index) {
72 None => {
73 // #2: If children iterator is exhausted, this means iteration of start node
74 // just finished, and the layer needs to be popped (i.e. not pushed back)
75 // and leave event for this node needs to be
76 // emitted.
77 Some(WalkEvent::Leave(layer.start))
78 }
79 Some(start) => {
80 // #3: Otherwise the iterator is just in the middle of visiting a child, so
81 // push a new layer to iterate it. To avoid
82 // recursion, step #1 is duplicated and
83 // inlined here.
84 let event = WalkEvent::Enter(*start);
85 let new_layer = PreorderLayer {
86 children: Some((start.get_children(self.db), 0)),
87 start: *start,
88 };
89 layer.children = Some((nodes, index + 1));
90 self.layers.extend([layer, new_layer]);
91 Some(event)
92 }
93 }
94 }
95 }
96 }
97}