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 serde::Serialize;
8
9use mago_database::file::FileId;
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    #[must_use]
41    pub const fn new(inner: Vec<'arena, T>) -> Self {
42        Self { nodes: inner }
43    }
44
45    #[inline]
46    pub fn empty(arena: &'arena Bump) -> Self {
47        Self { nodes: vec![in arena] }
48    }
49
50    #[inline]
51    #[must_use]
52    pub fn len(&self) -> usize {
53        self.nodes.len()
54    }
55
56    #[inline]
57    #[must_use]
58    pub fn is_empty(&self) -> bool {
59        self.nodes.is_empty()
60    }
61
62    #[inline]
63    #[must_use]
64    pub fn get(&self, index: usize) -> Option<&T> {
65        self.nodes.get(index)
66    }
67
68    #[inline]
69    #[must_use]
70    pub fn first(&self) -> Option<&T> {
71        self.nodes.first()
72    }
73
74    #[inline]
75    #[must_use]
76    pub fn first_span(&self) -> Option<Span> {
77        self.nodes.first().map(HasSpan::span)
78    }
79
80    #[inline]
81    #[must_use]
82    pub fn last(&self) -> Option<&T> {
83        self.nodes.last()
84    }
85
86    #[inline]
87    #[must_use]
88    pub fn last_span(&self) -> Option<Span> {
89        self.nodes.last().map(HasSpan::span)
90    }
91
92    #[inline]
93    #[must_use]
94    pub fn span(&self, file_id: FileId, from: Position) -> Span {
95        self.last_span().map_or(Span::new(file_id, from, from), |span| Span::new(file_id, from, span.end))
96    }
97
98    #[inline]
99    pub fn iter(&self) -> Iter<'_, T> {
100        self.nodes.iter()
101    }
102
103    #[inline]
104    #[must_use]
105    pub fn as_slice(&self) -> &[T] {
106        self.nodes.as_slice()
107    }
108}
109
110impl<'arena, T: HasSpan> TokenSeparatedSequence<'arena, T> {
111    #[inline]
112    #[must_use]
113    pub const fn new(inner: Vec<'arena, T>, tokens: Vec<'arena, Token>) -> Self {
114        Self { nodes: inner, tokens }
115    }
116
117    #[inline]
118    #[must_use]
119    pub fn empty(arena: &'arena Bump) -> Self {
120        Self { nodes: vec![in arena], tokens: vec![in arena] }
121    }
122
123    #[inline]
124    #[must_use]
125    pub fn len(&self) -> usize {
126        self.nodes.len()
127    }
128
129    #[inline]
130    #[must_use]
131    pub fn is_empty(&self) -> bool {
132        self.nodes.is_empty()
133    }
134
135    #[inline]
136    #[must_use]
137    pub fn get(&self, index: usize) -> Option<&T> {
138        self.nodes.get(index)
139    }
140
141    #[inline]
142    #[must_use]
143    pub fn first(&self) -> Option<&T> {
144        self.nodes.first()
145    }
146
147    #[inline]
148    #[must_use]
149    pub fn first_span(&self) -> Option<Span> {
150        match (self.tokens.first(), self.nodes.first()) {
151            (Some(token), Some(node)) => {
152                // check if the token comes before the node
153                if token.span.end <= node.span().start { Some(token.span) } else { Some(node.span()) }
154            }
155            (Some(token), None) => Some(token.span),
156            (None, Some(node)) => Some(node.span()),
157            (None, None) => None,
158        }
159    }
160
161    #[inline]
162    #[must_use]
163    pub fn last(&self) -> Option<&T> {
164        self.nodes.last()
165    }
166
167    #[inline]
168    #[must_use]
169    pub fn last_span(&self) -> Option<Span> {
170        match (self.tokens.last(), self.nodes.last()) {
171            (Some(token), Some(node)) => {
172                // check if the token comes after the node
173                if token.span.start >= node.span().end { Some(token.span) } else { Some(node.span()) }
174            }
175            (Some(token), None) => Some(token.span),
176            (None, Some(node)) => Some(node.span()),
177            (None, None) => None,
178        }
179    }
180
181    #[inline]
182    #[must_use]
183    pub fn span(&self, file_id: FileId, from: Position) -> Span {
184        match (self.first_span(), self.last_span()) {
185            (Some(first), Some(last)) => Span::new(file_id, first.start, last.end),
186            _ => Span::new(file_id, from, from),
187        }
188    }
189
190    #[inline]
191    #[must_use]
192    pub fn has_trailing_token(&self) -> bool {
193        self.tokens
194            .last()
195            .is_some_and(|token| token.span.start.offset >= self.nodes.last().map_or(0, |node| node.span().end.offset))
196    }
197
198    #[inline]
199    #[must_use]
200    pub fn get_trailing_token(&self) -> Option<&Token<'arena>> {
201        self.tokens
202            .last()
203            .filter(|token| token.span.start.offset >= self.nodes.last().map_or(0, |node| node.span().end.offset))
204    }
205
206    #[inline]
207    pub fn iter(&self) -> Iter<'_, T> {
208        self.nodes.iter()
209    }
210
211    /// Returns an iterator over the sequence, where each item includes
212    /// the index of the element, the element and the token following it.
213    /// The token is `None` only for the last element if it has no trailing token.
214    #[inline]
215    pub fn iter_with_tokens(&self) -> impl Iterator<Item = (usize, &T, Option<&Token<'arena>>)> {
216        self.nodes.iter().enumerate().map(move |(i, item)| {
217            let token = self.tokens.get(i);
218
219            (i, item, token)
220        })
221    }
222
223    #[inline]
224    #[must_use]
225    pub fn as_slice(&self) -> &[T] {
226        self.nodes.as_slice()
227    }
228}
229
230impl<'arena, T: HasSpan> IntoIterator for Sequence<'arena, T> {
231    type Item = T;
232    type IntoIter = IntoIter<'arena, Self::Item>;
233
234    fn into_iter(self) -> Self::IntoIter {
235        self.nodes.into_iter()
236    }
237}
238
239impl<'a, T: HasSpan> IntoIterator for &'a Sequence<'_, T> {
240    type Item = &'a T;
241    type IntoIter = Iter<'a, T>;
242
243    fn into_iter(self) -> Self::IntoIter {
244        self.iter()
245    }
246}
247
248impl<'arena, T: HasSpan> IntoIterator for TokenSeparatedSequence<'arena, T> {
249    type Item = T;
250    type IntoIter = IntoIter<'arena, Self::Item>;
251
252    fn into_iter(self) -> Self::IntoIter {
253        self.nodes.into_iter()
254    }
255}
256
257impl<'a, T: HasSpan> IntoIterator for &'a TokenSeparatedSequence<'_, T> {
258    type Item = &'a T;
259    type IntoIter = Iter<'a, T>;
260
261    fn into_iter(self) -> Self::IntoIter {
262        self.iter()
263    }
264}