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