Skip to main content

m2_syn/parser/
punctuated.rs

1//! Syntax sequences containing at least one typed punctuation token.
2
3use std::iter::{Chain, Map, Once, once};
4use std::slice;
5use std::slice::IterMut;
6use std::vec::IntoIter;
7
8use crate::{Lex, Parse, ParseStream, Spanned, Token, TokenParseError, TokenStream};
9
10/// Two syntax values paired by one punctuation token.
11///
12/// Unlike [`Punctuated`], this type represents exactly one relation: a left
13/// value, its separator, and a right value. It is useful when a grammar field
14/// requires exactly two operands rather than an arbitrarily long sequence.
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Spanned)]
16pub struct SeparatedPair<T, P = Token![,]> {
17    /// The value before the punctuation.
18    pub left: T,
19    /// The punctuation pairing the two values.
20    pub punctuation: P,
21    /// The value after the punctuation.
22    pub right: T,
23}
24
25impl<T, P> SeparatedPair<T, P> {
26    /// Creates a pair from its two values and their punctuation.
27    pub fn new(left: T, punctuation: P, right: T) -> Self {
28        Self {
29            left,
30            punctuation,
31            right,
32        }
33    }
34
35    /// Separates the pair into its two values and punctuation.
36    pub fn into_parts(self) -> (T, P, T) {
37        (self.left, self.punctuation, self.right)
38    }
39}
40
41impl<T: Parse, P: Parse> Parse for SeparatedPair<T, P> {
42    fn parse(input: &mut ParseStream) -> Result<Self, TokenParseError> {
43        Ok(Self::new(
44            T::parse(input)?,
45            P::parse(input)?,
46            T::parse(input)?,
47        ))
48    }
49}
50
51impl<T: Lex, P: Lex> Lex for SeparatedPair<T, P> {
52    fn to_tokens(&self, output: &mut TokenStream) {
53        self.left.to_tokens(output);
54        self.punctuation.to_tokens(output);
55        self.right.to_tokens(output);
56    }
57}
58
59/// Names or constructs a comma-punctuated sequence.
60///
61/// - `punc!(T)` names `Punctuated<T>`.
62/// - `punc!(pairs first; comma => second, ...)` constructs a sequence while
63///   retaining every supplied typed comma and its span.
64#[macro_export]
65macro_rules! punc {
66    (
67        pairs $first:expr;
68        $punctuation:expr => $second:expr
69        $(, $rest_punctuation:expr => $rest_value:expr)*
70        $(,)?
71    ) => {{
72        let mut values = $crate::Punctuated::new($first, $punctuation, $second);
73        $(values.push($rest_punctuation, $rest_value);)*
74        values
75    }};
76    ($value:ty) => {
77        $crate::Punctuated<$value>
78    };
79}
80
81/// Two or more syntax values separated by typed punctuation.
82///
83/// Every separator is stored as its actual token, including its source span.
84/// At least one separator and the values on both sides are mandatory. A single
85/// value belongs directly in the surrounding syntax type, while an optional
86/// whole sequence is `Option<Punctuated<T, P>>`.
87///
88/// ```
89/// use m2_syn::{Lex, Span};
90///
91/// type Pluses = m2_syn::punc!(m2_syn::Token![+]);
92/// let comma = m2_syn::Token![,](Span::for_text(","));
93/// let values: Pluses = m2_syn::punc!(
94///     pairs m2_syn::Token![+](Span::for_text("+"));
95///     comma => m2_syn::Token![+](Span::for_text("+")),
96/// );
97/// assert_eq!(values.to_m2(), "+,+");
98/// ```
99#[derive(Debug, Clone, PartialEq, Eq, Spanned)]
100pub struct Punctuated<S, P = Token![,]> {
101    /// The first.
102    first: S,
103    /// The second.
104    second: (P, S),
105    /// The rest.
106    rest: Vec<(P, S)>,
107}
108
109impl<S, P> Punctuated<S, P> {
110    /// Creates a sequence from the first two values and their punctuation.
111    pub fn new(first: S, punctuation: P, second: S) -> Self {
112        Self {
113            first,
114            second: (punctuation, second),
115            rest: Vec::new(),
116        }
117    }
118
119    /// Creates a two-value sequence from one punctuated pair.
120    pub fn from_pair(pair: SeparatedPair<S, P>) -> Self {
121        Self::from_pair_and_rest(pair, Vec::new())
122    }
123
124    /// Creates a sequence from its first separated pair and any later
125    /// punctuation-value pairs.
126    pub fn from_pair_and_rest(pair: SeparatedPair<S, P>, rest: Vec<(P, S)>) -> Self {
127        let (left, punctuation, right) = pair.into_parts();
128        Self {
129            first: left,
130            second: (punctuation, right),
131            rest,
132        }
133    }
134
135    /// Returns the first value.
136    pub fn first(&self) -> &S {
137        &self.first
138    }
139
140    /// Returns the first value mutably.
141    pub fn first_mut(&mut self) -> &mut S {
142        &mut self.first
143    }
144
145    /// Borrows every punctuation-value pair following the first value.
146    pub fn pairs(&self) -> Pairs<'_, S, P> {
147        once(&self.second).chain(self.rest.iter())
148    }
149
150    /// Mutably borrows every punctuation-value pair following the first value.
151    pub fn pairs_mut(&mut self) -> PairsMut<'_, S, P> {
152        once(&mut self.second).chain(self.rest.iter_mut())
153    }
154
155    /// Separates the sequence into its first pair and any later pairs.
156    pub fn into_parts(self) -> (SeparatedPair<S, P>, Vec<(P, S)>) {
157        let (punctuation, second) = self.second;
158        (
159            SeparatedPair::new(self.first, punctuation, second),
160            self.rest,
161        )
162    }
163
164    /// Returns the number of values, excluding punctuation.
165    pub fn len(&self) -> usize {
166        self.rest.len() + 2
167    }
168
169    /// Returns `false`; a punctuated sequence always contains at least two values.
170    pub const fn is_empty(&self) -> bool {
171        false
172    }
173
174    /// Returns the number of values and punctuation tokens together.
175    pub fn token_len(&self) -> usize {
176        self.rest.len() * 2 + 3
177    }
178
179    /// Borrows the values without discarding or synthesizing punctuation.
180    pub fn iter(&self) -> Values<'_, S, P> {
181        once(&self.first).chain(
182            self.pairs()
183                .map(rest_value_ref::<S, P> as fn(&(P, S)) -> &S),
184        )
185    }
186
187    /// Mutably borrows the values without discarding or synthesizing
188    /// punctuation.
189    pub fn iter_mut(&mut self) -> ValuesMut<'_, S, P> {
190        once(&mut self.first).chain(
191            once(&mut self.second)
192                .chain(self.rest.iter_mut())
193                .map(rest_value_mut::<S, P> as fn(&mut (P, S)) -> &mut S),
194        )
195    }
196
197    /// Appends a real punctuation token and the value following it.
198    pub fn push(&mut self, punctuation: P, value: S) {
199        self.rest.push((punctuation, value));
200    }
201
202    /// Maps the values while retaining every punctuation token unchanged.
203    pub fn map_values<T>(self, mut map: impl FnMut(S) -> T) -> Punctuated<T, P> {
204        let (pair, rest) = self.into_parts();
205        let (first, punctuation, second) = pair.into_parts();
206        Punctuated::from_pair_and_rest(
207            SeparatedPair::new(map(first), punctuation, map(second)),
208            rest.into_iter()
209                .map(|(punctuation, value)| (punctuation, map(value)))
210                .collect(),
211        )
212    }
213
214    /// Fallibly maps both values and punctuation while retaining their order.
215    ///
216    /// # Errors
217    ///
218    /// Returns the first error produced by either mapping function.
219    pub fn try_map<T, Q, E>(
220        self,
221        mut map_value: impl FnMut(S) -> Result<T, E>,
222        mut map_punctuation: impl FnMut(P) -> Result<Q, E>,
223    ) -> Result<Punctuated<T, Q>, E> {
224        let (pair, rest) = self.into_parts();
225        let (first, punctuation, second) = pair.into_parts();
226        Ok(Punctuated::from_pair_and_rest(
227            SeparatedPair::new(
228                map_value(first)?,
229                map_punctuation(punctuation)?,
230                map_value(second)?,
231            ),
232            rest.into_iter()
233                .map(|(punctuation, value)| Ok((map_punctuation(punctuation)?, map_value(value)?)))
234                .collect::<Result<Vec<_>, E>>()?,
235        ))
236    }
237}
238
239impl<S: Lex, P: Lex> Lex for Punctuated<S, P> {
240    fn to_tokens(&self, output: &mut TokenStream) {
241        self.first.to_tokens(output);
242        for (punctuation, value) in self.pairs() {
243            punctuation.to_tokens(output);
244            value.to_tokens(output);
245        }
246    }
247}
248
249impl<S, P> Parse for Punctuated<S, P>
250where
251    S: Parse,
252    P: Parse + Token,
253{
254    fn parse(input: &mut ParseStream) -> Result<Self, TokenParseError> {
255        let first = S::parse(input)?;
256        let punctuation = P::parse(input)?;
257        let second = S::parse(input)?;
258        let mut values = Self::new(first, punctuation, second);
259        while input.peek().is_some_and(P::matches_token_tree) {
260            let punctuation = P::parse(input)?;
261            values.push(punctuation, S::parse(input)?);
262        }
263        Ok(values)
264    }
265}
266
267/// An iterator borrowing punctuation-value pairs from a [`Punctuated`] sequence.
268pub type Pairs<'a, S, P> = Chain<Once<&'a (P, S)>, slice::Iter<'a, (P, S)>>;
269
270/// An iterator mutably borrowing punctuation-value pairs from a [`Punctuated`] sequence.
271pub type PairsMut<'a, S, P> = Chain<Once<&'a mut (P, S)>, IterMut<'a, (P, S)>>;
272
273/// An iterator borrowing only the values of a [`Punctuated`] sequence.
274pub type Values<'a, S, P> = Chain<Once<&'a S>, Map<Pairs<'a, S, P>, fn(&(P, S)) -> &S>>;
275
276/// An iterator mutably borrowing only the values of a [`Punctuated`] sequence.
277pub type ValuesMut<'a, S, P> =
278    Chain<Once<&'a mut S>, Map<PairsMut<'a, S, P>, fn(&mut (P, S)) -> &mut S>>;
279
280/// An iterator owning punctuation-value pairs from a [`Punctuated`] sequence.
281pub type IntoPairs<S, P> = Chain<Once<(P, S)>, IntoIter<(P, S)>>;
282
283/// An iterator owning only the values of a [`Punctuated`] sequence.
284pub type IntoValues<S, P> = Chain<Once<S>, Map<IntoPairs<S, P>, fn((P, S)) -> S>>;
285
286impl<S, P> IntoIterator for Punctuated<S, P> {
287    type Item = S;
288    type IntoIter = IntoValues<S, P>;
289
290    fn into_iter(self) -> Self::IntoIter {
291        once(self.first).chain(
292            once(self.second)
293                .chain(self.rest)
294                .map(rest_value::<S, P> as fn((P, S)) -> S),
295        )
296    }
297}
298
299impl<'a, S, P> IntoIterator for &'a Punctuated<S, P> {
300    type Item = &'a S;
301    type IntoIter = Values<'a, S, P>;
302
303    fn into_iter(self) -> Self::IntoIter {
304        self.iter()
305    }
306}
307
308impl<'a, S, P> IntoIterator for &'a mut Punctuated<S, P> {
309    type Item = &'a mut S;
310    type IntoIter = ValuesMut<'a, S, P>;
311
312    fn into_iter(self) -> Self::IntoIter {
313        self.iter_mut()
314    }
315}
316
317/// Handles rest value.
318fn rest_value<S, P>((_, value): (P, S)) -> S {
319    value
320}
321
322/// Handles rest value ref.
323fn rest_value_ref<S, P>((_, value): &(P, S)) -> &S {
324    value
325}
326
327/// Handles rest value mut.
328fn rest_value_mut<S, P>((_, value): &mut (P, S)) -> &mut S {
329    value
330}
331
332#[cfg(test)]
333mod tests {
334    use super::*;
335
336    #[test]
337    fn a_sequence_is_nonempty_and_retains_separator_spans() {
338        let source = "+,+";
339        let values: Punctuated<Token![+], Token![,]> = crate::parse_fragment_str(source).unwrap();
340        let comma_span = values.pairs().next().unwrap().0.expect_span();
341
342        assert_eq!(values.len(), 2);
343        assert_eq!(values.token_len(), 3);
344        assert_eq!(comma_span.text(), Some(","));
345        assert_eq!(comma_span.bounds(), Some((1, 2)));
346        assert_eq!(values.to_m2(), "+,+");
347    }
348
349    #[test]
350    fn a_single_value_is_not_a_punctuated_sequence() {
351        let result = crate::parse_fragment_str::<Punctuated<Token![+]>>("+");
352
353        assert!(result.is_err());
354    }
355}