Skip to main content

mago_syntax_core/cst/
mod.rs

1//! Shared AST primitives used across every syntax crate.
2
3use std::slice::Iter;
4
5use mago_allocator::prelude::*;
6
7use mago_span::HasPosition;
8use mago_span::HasSpan;
9use mago_span::Span;
10
11/// A sequence of AST nodes allocated in an arena.
12#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord)]
13#[cfg_attr(feature = "serde", derive(serde::Serialize))]
14#[repr(transparent)]
15pub struct Sequence<'arena, T> {
16    pub nodes: &'arena [T],
17}
18
19impl<'arena, T> Sequence<'arena, T> {
20    /// Freezes an arena-allocated vector into a sequence.
21    #[inline]
22    #[must_use]
23    pub fn new<A>(nodes: Vec<'arena, T, A>) -> Self
24    where
25        A: Arena,
26    {
27        Self { nodes: nodes.leak() }
28    }
29
30    /// Wraps an existing arena slice as a sequence.
31    #[inline]
32    #[must_use]
33    pub const fn from_slice(nodes: &'arena [T]) -> Self {
34        Self { nodes }
35    }
36
37    #[inline]
38    #[must_use]
39    pub const fn empty() -> Self {
40        Self { nodes: &[] }
41    }
42
43    #[inline]
44    #[must_use]
45    pub fn len(&self) -> usize {
46        self.nodes.len()
47    }
48
49    #[inline]
50    #[must_use]
51    pub fn is_empty(&self) -> bool {
52        self.nodes.is_empty()
53    }
54
55    #[inline]
56    #[must_use]
57    pub fn get(&self, index: usize) -> Option<&T> {
58        self.nodes.get(index)
59    }
60
61    #[inline]
62    #[must_use]
63    pub fn first(&self) -> Option<&T> {
64        self.nodes.first()
65    }
66
67    #[inline]
68    #[must_use]
69    pub fn last(&self) -> Option<&T> {
70        self.nodes.last()
71    }
72
73    #[inline]
74    pub fn iter(&self) -> Iter<'_, T> {
75        self.nodes.iter()
76    }
77
78    #[inline]
79    #[must_use]
80    pub fn as_slice(&self) -> &[T] {
81        self.nodes
82    }
83}
84
85impl<T: HasSpan> Sequence<'_, T> {
86    #[inline]
87    #[must_use]
88    pub fn first_span(&self) -> Option<Span> {
89        self.nodes.first().map(HasSpan::span)
90    }
91
92    #[inline]
93    #[must_use]
94    pub fn last_span(&self) -> Option<Span> {
95        self.nodes.last().map(HasSpan::span)
96    }
97
98    /// Compute the span covering the sequence, anchored at `from`.
99    ///
100    /// Returns a zero-width span at `from` when the sequence is empty.
101    #[inline]
102    #[must_use]
103    pub fn span(&self, file_id: mago_database::file::FileId, from: mago_span::Position) -> Span {
104        self.last_span().map_or(Span::new(file_id, from, from), |span| Span::new(file_id, from, span.end))
105    }
106}
107
108impl<T> std::ops::Index<usize> for Sequence<'_, T> {
109    type Output = T;
110
111    #[inline]
112    fn index(&self, index: usize) -> &T {
113        &self.nodes[index]
114    }
115}
116
117impl<T, Tok> std::ops::Index<usize> for TokenSeparatedSequence<'_, T, Tok> {
118    type Output = T;
119
120    #[inline]
121    fn index(&self, index: usize) -> &T {
122        &self.nodes[index]
123    }
124}
125
126impl<'arena, T> IntoIterator for Sequence<'arena, T> {
127    type Item = &'arena T;
128    type IntoIter = Iter<'arena, T>;
129
130    fn into_iter(self) -> Self::IntoIter {
131        self.nodes.iter()
132    }
133}
134
135impl<'seq, T> IntoIterator for &'seq Sequence<'_, T> {
136    type Item = &'seq T;
137    type IntoIter = Iter<'seq, T>;
138
139    fn into_iter(self) -> Self::IntoIter {
140        self.iter()
141    }
142}
143
144/// A sequence of AST nodes separated by infix tokens.
145///
146/// The token type is generic so every syntax crate can plug in its own
147/// token definition. Methods that depend on spatial relationships between
148/// nodes and tokens require [`HasPosition`] on the token; that's the
149/// narrowest bound that still supports `has_trailing_token` style
150/// queries, and every sibling crate's token already carries a [`Position`]
151/// start so the impl is trivial.
152///
153/// [`Position`]: mago_span::Position
154#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
155#[cfg_attr(feature = "serde", derive(serde::Serialize))]
156pub struct TokenSeparatedSequence<'arena, T, Tok> {
157    pub nodes: &'arena [T],
158    pub tokens: &'arena [Tok],
159}
160
161impl<'arena, T, Tok> TokenSeparatedSequence<'arena, T, Tok> {
162    /// Freezes arena-allocated node and token vectors into a separated sequence.
163    #[inline]
164    #[must_use]
165    pub fn new<A>(nodes: Vec<'arena, T, A>, tokens: Vec<'arena, Tok, A>) -> Self
166    where
167        A: Arena,
168    {
169        Self { nodes: nodes.leak(), tokens: tokens.leak() }
170    }
171
172    /// Wraps existing arena slices as a separated sequence.
173    #[inline]
174    #[must_use]
175    pub const fn from_slices(nodes: &'arena [T], tokens: &'arena [Tok]) -> Self {
176        Self { nodes, tokens }
177    }
178
179    #[inline]
180    #[must_use]
181    pub const fn empty() -> Self {
182        Self { nodes: &[], tokens: &[] }
183    }
184
185    #[inline]
186    #[must_use]
187    pub fn len(&self) -> usize {
188        self.nodes.len()
189    }
190
191    #[inline]
192    #[must_use]
193    pub fn is_empty(&self) -> bool {
194        self.nodes.is_empty()
195    }
196
197    #[inline]
198    #[must_use]
199    pub fn get(&self, index: usize) -> Option<&T> {
200        self.nodes.get(index)
201    }
202
203    #[inline]
204    #[must_use]
205    pub fn first(&self) -> Option<&T> {
206        self.nodes.first()
207    }
208
209    #[inline]
210    #[must_use]
211    pub fn last(&self) -> Option<&T> {
212        self.nodes.last()
213    }
214
215    #[inline]
216    pub fn iter(&self) -> Iter<'_, T> {
217        self.nodes.iter()
218    }
219
220    #[inline]
221    #[must_use]
222    pub fn as_slice(&self) -> &[T] {
223        self.nodes
224    }
225
226    /// Iterate yielding `(index, node, optional trailing token)` tuples.
227    ///
228    /// The token is `None` only for the last element if it has no trailing
229    /// separator.
230    #[inline]
231    pub fn iter_with_tokens(&self) -> impl Iterator<Item = (usize, &T, Option<&Tok>)> {
232        self.nodes.iter().enumerate().map(move |(i, item)| (i, item, self.tokens.get(i)))
233    }
234}
235
236impl<T, Tok> TokenSeparatedSequence<'_, T, Tok>
237where
238    T: HasSpan,
239    Tok: HasPosition,
240{
241    /// Whether the sequence ends with a trailing separator token.
242    #[inline]
243    #[must_use]
244    pub fn has_trailing_token(&self) -> bool {
245        self.tokens.last().is_some_and(|t| t.offset() >= self.nodes.last().map_or(0, |n| n.span().end.offset))
246    }
247
248    /// Return the trailing separator token, if any.
249    #[inline]
250    #[must_use]
251    pub fn get_trailing_token(&self) -> Option<&Tok> {
252        self.tokens.last().filter(|t| t.offset() >= self.nodes.last().map_or(0, |n| n.span().end.offset))
253    }
254}
255
256impl<'arena, T, Tok> IntoIterator for TokenSeparatedSequence<'arena, T, Tok> {
257    type Item = &'arena T;
258    type IntoIter = Iter<'arena, T>;
259
260    fn into_iter(self) -> Self::IntoIter {
261        self.nodes.iter()
262    }
263}
264
265impl<'seq, T, Tok> IntoIterator for &'seq TokenSeparatedSequence<'_, T, Tok> {
266    type Item = &'seq T;
267    type IntoIter = Iter<'seq, T>;
268
269    fn into_iter(self) -> Self::IntoIter {
270        self.iter()
271    }
272}