Skip to main content

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, file_id: FileId) -> 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                let token_end = token.start.offset + token.value.len() as u32;
154                if token_end <= node.span().start.offset { Some(token.span_for(file_id)) } else { Some(node.span()) }
155            }
156            (Some(token), None) => Some(token.span_for(file_id)),
157            (None, Some(node)) => Some(node.span()),
158            (None, None) => None,
159        }
160    }
161
162    #[inline]
163    #[must_use]
164    pub fn last(&self) -> Option<&T> {
165        self.nodes.last()
166    }
167
168    #[inline]
169    #[must_use]
170    pub fn last_span(&self, file_id: FileId) -> Option<Span> {
171        match (self.tokens.last(), self.nodes.last()) {
172            (Some(token), Some(node)) => {
173                // check if the token comes after the node
174                if token.start.offset >= node.span().end.offset {
175                    Some(token.span_for(file_id))
176                } else {
177                    Some(node.span())
178                }
179            }
180            (Some(token), None) => Some(token.span_for(file_id)),
181            (None, Some(node)) => Some(node.span()),
182            (None, None) => None,
183        }
184    }
185
186    #[inline]
187    #[must_use]
188    pub fn span(&self, file_id: FileId, from: Position) -> Span {
189        match (self.first_span(file_id), self.last_span(file_id)) {
190            (Some(first), Some(last)) => Span::new(file_id, first.start, last.end),
191            _ => Span::new(file_id, from, from),
192        }
193    }
194
195    #[inline]
196    #[must_use]
197    pub fn has_trailing_token(&self) -> bool {
198        self.tokens
199            .last()
200            .is_some_and(|token| token.start.offset >= self.nodes.last().map_or(0, |node| node.span().end.offset))
201    }
202
203    #[inline]
204    #[must_use]
205    pub fn get_trailing_token(&self) -> Option<&Token<'arena>> {
206        self.tokens
207            .last()
208            .filter(|token| token.start.offset >= self.nodes.last().map_or(0, |node| node.span().end.offset))
209    }
210
211    #[inline]
212    pub fn iter(&self) -> Iter<'_, T> {
213        self.nodes.iter()
214    }
215
216    /// Returns an iterator over the sequence, where each item includes
217    /// the index of the element, the element and the token following it.
218    /// The token is `None` only for the last element if it has no trailing token.
219    #[inline]
220    pub fn iter_with_tokens(&self) -> impl Iterator<Item = (usize, &T, Option<&Token<'arena>>)> {
221        self.nodes.iter().enumerate().map(move |(i, item)| {
222            let token = self.tokens.get(i);
223
224            (i, item, token)
225        })
226    }
227
228    #[inline]
229    #[must_use]
230    pub fn as_slice(&self) -> &[T] {
231        self.nodes.as_slice()
232    }
233}
234
235impl<'arena, T: HasSpan> IntoIterator for Sequence<'arena, T> {
236    type Item = T;
237    type IntoIter = IntoIter<'arena, Self::Item>;
238
239    fn into_iter(self) -> Self::IntoIter {
240        self.nodes.into_iter()
241    }
242}
243
244impl<'a, T: HasSpan> IntoIterator for &'a Sequence<'_, T> {
245    type Item = &'a T;
246    type IntoIter = Iter<'a, T>;
247
248    fn into_iter(self) -> Self::IntoIter {
249        self.iter()
250    }
251}
252
253impl<'arena, T: HasSpan> IntoIterator for TokenSeparatedSequence<'arena, T> {
254    type Item = T;
255    type IntoIter = IntoIter<'arena, Self::Item>;
256
257    fn into_iter(self) -> Self::IntoIter {
258        self.nodes.into_iter()
259    }
260}
261
262impl<'a, T: HasSpan> IntoIterator for &'a TokenSeparatedSequence<'_, T> {
263    type Item = &'a T;
264    type IntoIter = Iter<'a, T>;
265
266    fn into_iter(self) -> Self::IntoIter {
267        self.iter()
268    }
269}