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