mago_syntax/ast/
sequence.rs

1use std::slice::Iter;
2
3use bumpalo::Bump;
4use bumpalo::collections::Vec;
5use bumpalo::collections::vec::IntoIter;
6use bumpalo::vec;
7use mago_database::file::FileId;
8use serde::Serialize;
9
10use mago_span::HasSpan;
11use mago_span::Position;
12use mago_span::Span;
13
14use crate::token::Token;
15
16/// Represents a sequence of nodes.
17///
18/// An example of this is modifiers in a method declaration.
19///
20/// i.e. `public` and `static` in `public static function foo() {}`.
21#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, PartialOrd, Ord)]
22#[repr(transparent)]
23pub struct Sequence<'arena, T> {
24    pub nodes: Vec<'arena, T>,
25}
26
27/// Represents a sequence of nodes separated by a token.
28///
29/// An example of this is arguments in a function call, where the tokens are commas.
30///
31/// i.e. `1`, `2` and `3` in `foo(1, 2, 3)`.
32#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, PartialOrd, Ord)]
33pub struct TokenSeparatedSequence<'arena, T> {
34    pub nodes: Vec<'arena, T>,
35    pub tokens: Vec<'arena, Token<'arena>>,
36}
37
38impl<'arena, T: HasSpan> Sequence<'arena, T> {
39    #[inline]
40    pub const fn new(inner: Vec<'arena, T>) -> Self {
41        Self { nodes: inner }
42    }
43
44    #[inline]
45    pub fn empty(arena: &'arena Bump) -> Self {
46        Self { nodes: vec![in arena] }
47    }
48
49    #[inline]
50    pub fn len(&self) -> usize {
51        self.nodes.len()
52    }
53
54    #[inline]
55    pub fn is_empty(&self) -> bool {
56        self.nodes.is_empty()
57    }
58
59    #[inline]
60    #[must_use]
61    pub fn get(&self, index: usize) -> Option<&T> {
62        self.nodes.get(index)
63    }
64
65    #[inline]
66    #[must_use]
67    pub fn first(&self) -> Option<&T> {
68        self.nodes.first()
69    }
70
71    #[inline]
72    #[must_use]
73    pub fn first_span(&self) -> Option<Span> {
74        self.nodes.first().map(|node| node.span())
75    }
76
77    #[inline]
78    #[must_use]
79    pub fn last(&self) -> Option<&T> {
80        self.nodes.last()
81    }
82
83    #[inline]
84    #[must_use]
85    pub fn last_span(&self) -> Option<Span> {
86        self.nodes.last().map(|node| node.span())
87    }
88
89    #[inline]
90    #[must_use]
91    pub fn span(&self, file_id: FileId, from: Position) -> Span {
92        self.last_span().map_or(Span::new(file_id, from, from), |span| Span::new(file_id, from, span.end))
93    }
94
95    #[inline]
96    pub fn iter(&self) -> Iter<'_, T> {
97        self.nodes.iter()
98    }
99
100    #[inline]
101    #[must_use]
102    pub fn as_slice(&self) -> &[T] {
103        self.nodes.as_slice()
104    }
105}
106
107impl<'arena, T: HasSpan> TokenSeparatedSequence<'arena, T> {
108    #[inline]
109    #[must_use]
110    pub const fn new(inner: Vec<'arena, T>, tokens: Vec<'arena, Token>) -> Self {
111        Self { nodes: inner, tokens }
112    }
113
114    #[inline]
115    #[must_use]
116    pub fn empty(arena: &'arena Bump) -> Self {
117        Self { nodes: vec![in arena], tokens: vec![in arena] }
118    }
119
120    #[inline]
121    pub fn len(&self) -> usize {
122        self.nodes.len()
123    }
124
125    #[inline]
126    pub fn is_empty(&self) -> bool {
127        self.nodes.is_empty()
128    }
129
130    #[inline]
131    pub fn get(&self, index: usize) -> Option<&T> {
132        self.nodes.get(index)
133    }
134
135    #[inline]
136    #[must_use]
137    pub fn first(&self) -> Option<&T> {
138        self.nodes.first()
139    }
140
141    #[inline]
142    pub fn first_span(&self) -> Option<Span> {
143        match (self.tokens.first(), self.nodes.first()) {
144            (Some(token), Some(node)) => {
145                // check if the token comes before the node
146                if token.span.end <= node.span().start { Some(token.span) } else { Some(node.span()) }
147            }
148            (Some(token), None) => Some(token.span),
149            (None, Some(node)) => Some(node.span()),
150            (None, None) => None,
151        }
152    }
153
154    #[inline]
155    pub fn last(&self) -> Option<&T> {
156        self.nodes.last()
157    }
158
159    #[inline]
160    pub fn last_span(&self) -> Option<Span> {
161        match (self.tokens.last(), self.nodes.last()) {
162            (Some(token), Some(node)) => {
163                // check if the token comes after the node
164                if token.span.start >= node.span().end { Some(token.span) } else { Some(node.span()) }
165            }
166            (Some(token), None) => Some(token.span),
167            (None, Some(node)) => Some(node.span()),
168            (None, None) => None,
169        }
170    }
171
172    #[inline]
173    pub fn span(&self, file_id: FileId, from: Position) -> Span {
174        match (self.first_span(), self.last_span()) {
175            (Some(first), Some(last)) => Span::new(file_id, first.start, last.end),
176            _ => Span::new(file_id, from, from),
177        }
178    }
179
180    #[inline]
181    pub fn has_trailing_token(&self) -> bool {
182        self.tokens
183            .last()
184            .is_some_and(|token| token.span.start.offset >= self.nodes.last().map_or(0, |node| node.span().end.offset))
185    }
186
187    #[inline]
188    pub fn get_trailing_token(&self) -> Option<&Token<'arena>> {
189        self.tokens
190            .last()
191            .filter(|token| token.span.start.offset >= self.nodes.last().map_or(0, |node| node.span().end.offset))
192    }
193
194    #[inline]
195    pub fn iter<'ast>(&'ast self) -> Iter<'ast, T> {
196        self.nodes.iter()
197    }
198
199    /// Returns an iterator over the sequence, where each item includes
200    /// the index of the element, the element and the token following it.
201    /// The token is `None` only for the last element if it has no trailing token.
202    #[inline]
203    pub fn iter_with_tokens(&self) -> impl Iterator<Item = (usize, &T, Option<&Token<'arena>>)> {
204        self.nodes.iter().enumerate().map(move |(i, item)| {
205            let token = self.tokens.get(i);
206
207            (i, item, token)
208        })
209    }
210
211    #[inline]
212    pub fn as_slice(&self) -> &[T] {
213        self.nodes.as_slice()
214    }
215}
216
217impl<'arena, T: HasSpan> IntoIterator for Sequence<'arena, T> {
218    type Item = T;
219    type IntoIter = IntoIter<'arena, Self::Item>;
220
221    fn into_iter(self) -> Self::IntoIter {
222        self.nodes.into_iter()
223    }
224}
225
226impl<'arena, T: HasSpan> IntoIterator for TokenSeparatedSequence<'arena, T> {
227    type Item = T;
228    type IntoIter = IntoIter<'arena, Self::Item>;
229
230    fn into_iter(self) -> Self::IntoIter {
231        self.nodes.into_iter()
232    }
233}