cairo_lang_syntax/node/
with_db.rs

1use cairo_lang_filesystem::span::TextWidth;
2use cairo_lang_primitive_token::{PrimitiveSpan, PrimitiveToken, ToPrimitiveTokenStream};
3use salsa::Database;
4
5use super::SyntaxNode;
6
7pub struct SyntaxNodeWithDb<'a> {
8    node: &'a SyntaxNode<'a>,
9    db: &'a dyn Database,
10}
11
12impl<'a> SyntaxNodeWithDb<'a> {
13    pub fn new(node: &'a SyntaxNode<'a>, db: &'a dyn Database) -> Self {
14        Self { node, db }
15    }
16}
17
18impl<'a> ToPrimitiveTokenStream for SyntaxNodeWithDb<'a> {
19    type Iter = SyntaxNodeWithDbIterator<'a>;
20
21    fn to_primitive_token_stream(&self) -> Self::Iter {
22        // The lifetime of the iterator should extend 'a because it derives from both node and db
23        SyntaxNodeWithDbIterator::new(self.db, self.node)
24    }
25}
26
27pub struct SyntaxNodeWithDbIterator<'a> {
28    /// Stack used for driving iterative depth-first traversal of the syntax tree.
29    iter_stack: Vec<&'a SyntaxNode<'a>>,
30    /// Each step of the traversal may yield up to three tokens, so we collect them in this buffer.
31    /// **INVARIANT**: The collection to the buffer only happens when the buffer was previously
32    /// empty.
33    buffer: Vec<PrimitiveToken>,
34    db: &'a dyn Database,
35}
36
37impl<'a> SyntaxNodeWithDbIterator<'a> {
38    pub fn new(db: &'a dyn Database, node: &'a SyntaxNode<'a>) -> Self {
39        Self { db, buffer: Vec::with_capacity(3), iter_stack: vec![node] }
40    }
41}
42
43impl<'a> Iterator for SyntaxNodeWithDbIterator<'a> {
44    type Item = PrimitiveToken;
45
46    fn next(&mut self) -> Option<Self::Item> {
47        // If the buffer is empty, perform a single step of the depth-first traversal.
48        // This will fill the buffer with up to three tokens from the current node.
49        // If the stack is empty, it means we have traversed the entire tree and the function will
50        // return `None`, ending the iteration.
51        loop {
52            // INVARIANT: Empty the buffer before traversing further.
53            if let Some(item) = self.buffer.pop() {
54                // Return the next token from the buffer.
55                return Some(item);
56            }
57            let node = self.iter_stack.pop()?;
58            // This represents a single step of the depth-first traversal of a syntax tree.
59            // If the node is a terminal, it creates and saves token representation of it.
60            // Otherwise, it pushes all children of the node onto the iteration stack.
61            if node.green_node(self.db).kind.is_terminal() {
62                token_from_syntax_node(node, self.db, &mut self.buffer);
63            } else {
64                self.iter_stack.extend(node.get_children(self.db).iter().rev());
65            }
66        }
67    }
68}
69
70/// Create a `PrimitiveToken` representation from a `SyntaxNode`.
71///
72/// The `PrimitiveToken` keeps two information - some text content and a span associated with it.
73/// A `SyntaxNode` consists of some token, trivia associated with this token, and a span describing
74/// the token either with or without trivia.
75/// We split the content of the `SyntaxNode` into three parts: the prefix trivia, the main content
76/// of the node, and the suffix trivia. Each part is represented as a separate `PrimitiveToken`
77/// with its corresponding span.
78///
79/// Each of these `PrimitiveToken` structs is pushed into the `result` queue.
80///
81/// **INVARIANT**: `result` **MUST** be empty when passed to this function.
82fn token_from_syntax_node(
83    node: &SyntaxNode<'_>,
84    db: &dyn Database,
85    result: &mut Vec<PrimitiveToken>,
86) {
87    let span_without_trivia = node.span_without_trivia(db);
88    let span_with_trivia = node.span(db);
89    let text = node.get_text(db);
90    let prefix_len = span_without_trivia.start - span_with_trivia.start;
91    let (prefix, rest) = text.split_at(prefix_len.as_u32() as usize);
92    let suffix_len = span_with_trivia.end - span_without_trivia.end;
93    let (content, suffix) = rest.split_at(rest.len() - suffix_len.as_u32() as usize);
94    if suffix_len > TextWidth::ZERO {
95        result.push(PrimitiveToken {
96            content: suffix.to_string(),
97            span: Some(PrimitiveSpan {
98                start: span_without_trivia.end.as_u32() as usize,
99                end: span_with_trivia.end.as_u32() as usize,
100            }),
101        });
102    }
103    if !content.is_empty() {
104        result.push(PrimitiveToken {
105            content: content.to_string(),
106            span: Some(PrimitiveSpan {
107                start: span_without_trivia.start.as_u32() as usize,
108                end: span_without_trivia.end.as_u32() as usize,
109            }),
110        });
111    }
112    if prefix_len > TextWidth::ZERO {
113        result.push(PrimitiveToken {
114            content: prefix.to_string(),
115            span: Some(PrimitiveSpan {
116                start: span_with_trivia.start.as_u32() as usize,
117                end: span_without_trivia.start.as_u32() as usize,
118            }),
119        });
120    }
121}