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    first: S,
102    second: (P, S),
103    rest: Vec<(P, S)>,
104}
105
106impl<S, P> Punctuated<S, P> {
107    /// Creates a sequence from the first two values and their punctuation.
108    pub fn new(first: S, punctuation: P, second: S) -> Self {
109        Self {
110            first,
111            second: (punctuation, second),
112            rest: Vec::new(),
113        }
114    }
115
116    /// Creates a two-value sequence from one punctuated pair.
117    pub fn from_pair(pair: SeparatedPair<S, P>) -> Self {
118        Self::from_pair_and_rest(pair, Vec::new())
119    }
120
121    /// Creates a sequence from its first separated pair and any later
122    /// punctuation-value pairs.
123    pub fn from_pair_and_rest(pair: SeparatedPair<S, P>, rest: Vec<(P, S)>) -> Self {
124        let (left, punctuation, right) = pair.into_parts();
125        Self {
126            first: left,
127            second: (punctuation, right),
128            rest,
129        }
130    }
131
132    /// Returns the first value.
133    pub fn first(&self) -> &S {
134        &self.first
135    }
136
137    /// Returns the first value mutably.
138    pub fn first_mut(&mut self) -> &mut S {
139        &mut self.first
140    }
141
142    /// Borrows every punctuation-value pair following the first value.
143    pub fn pairs(&self) -> Pairs<'_, S, P> {
144        once(&self.second).chain(self.rest.iter())
145    }
146
147    /// Mutably borrows every punctuation-value pair following the first value.
148    pub fn pairs_mut(&mut self) -> PairsMut<'_, S, P> {
149        once(&mut self.second).chain(self.rest.iter_mut())
150    }
151
152    /// Separates the sequence into its first pair and any later pairs.
153    pub fn into_parts(self) -> (SeparatedPair<S, P>, Vec<(P, S)>) {
154        let (punctuation, second) = self.second;
155        (
156            SeparatedPair::new(self.first, punctuation, second),
157            self.rest,
158        )
159    }
160
161    /// Returns the number of values, excluding punctuation.
162    pub fn len(&self) -> usize {
163        self.rest.len() + 2
164    }
165
166    /// Returns `false`; a punctuated sequence always contains at least two values.
167    pub const fn is_empty(&self) -> bool {
168        false
169    }
170
171    /// Returns the number of values and punctuation tokens together.
172    pub fn token_len(&self) -> usize {
173        self.rest.len() * 2 + 3
174    }
175
176    /// Borrows the values without discarding or synthesizing punctuation.
177    pub fn iter(&self) -> Values<'_, S, P> {
178        once(&self.first).chain(
179            self.pairs()
180                .map(rest_value_ref::<S, P> as fn(&(P, S)) -> &S),
181        )
182    }
183
184    /// Mutably borrows the values without discarding or synthesizing
185    /// punctuation.
186    pub fn iter_mut(&mut self) -> ValuesMut<'_, S, P> {
187        once(&mut self.first).chain(
188            once(&mut self.second)
189                .chain(self.rest.iter_mut())
190                .map(rest_value_mut::<S, P> as fn(&mut (P, S)) -> &mut S),
191        )
192    }
193
194    /// Appends a real punctuation token and the value following it.
195    pub fn push(&mut self, punctuation: P, value: S) {
196        self.rest.push((punctuation, value));
197    }
198
199    /// Maps the values while retaining every punctuation token unchanged.
200    pub fn map_values<T>(self, mut map: impl FnMut(S) -> T) -> Punctuated<T, P> {
201        let (pair, rest) = self.into_parts();
202        let (first, punctuation, second) = pair.into_parts();
203        Punctuated::from_pair_and_rest(
204            SeparatedPair::new(map(first), punctuation, map(second)),
205            rest.into_iter()
206                .map(|(punctuation, value)| (punctuation, map(value)))
207                .collect(),
208        )
209    }
210
211    /// Fallibly maps both values and punctuation while retaining their order.
212    ///
213    /// # Errors
214    ///
215    /// Returns the first error produced by either mapping function.
216    pub fn try_map<T, Q, E>(
217        self,
218        mut map_value: impl FnMut(S) -> Result<T, E>,
219        mut map_punct: impl FnMut(P) -> Result<Q, E>,
220    ) -> Result<Punctuated<T, Q>, E> {
221        let (pair, rest) = self.into_parts();
222        let (first, punct, second) = pair.into_parts();
223        Ok(Punctuated::from_pair_and_rest(
224            SeparatedPair::new(map_value(first)?, map_punct(punct)?, map_value(second)?),
225            rest.into_iter()
226                .map(|(punct, value)| Ok((map_punct(punct)?, map_value(value)?)))
227                .collect::<Result<Vec<_>, E>>()?,
228        ))
229    }
230}
231
232impl<S: Lex, P: Lex> Lex for Punctuated<S, P> {
233    fn to_tokens(&self, output: &mut TokenStream) {
234        self.first.to_tokens(output);
235        for (punct, value) in self.pairs() {
236            punct.to_tokens(output);
237            value.to_tokens(output);
238        }
239    }
240}
241
242impl<S, P> Parse for Punctuated<S, P>
243where
244    S: Parse,
245    P: Parse + Token,
246{
247    fn parse(input: &mut ParseStream) -> Result<Self, TokenParseError> {
248        let first = S::parse(input)?;
249        let punct = P::parse(input)?;
250        let second = S::parse(input)?;
251        let mut values = Self::new(first, punct, second);
252        while input.peek().is_some_and(P::matches_token_tree) {
253            let punct = P::parse(input)?;
254            values.push(punct, S::parse(input)?);
255        }
256        Ok(values)
257    }
258}
259
260/// An iterator borrowing punctuation-value pairs from a [`Punctuated`] sequence.
261pub type Pairs<'a, S, P> = Chain<Once<&'a (P, S)>, slice::Iter<'a, (P, S)>>;
262
263/// An iterator mutably borrowing punctuation-value pairs from a [`Punctuated`] sequence.
264pub type PairsMut<'a, S, P> = Chain<Once<&'a mut (P, S)>, IterMut<'a, (P, S)>>;
265
266/// An iterator borrowing only the values of a [`Punctuated`] sequence.
267pub type Values<'a, S, P> = Chain<Once<&'a S>, Map<Pairs<'a, S, P>, fn(&(P, S)) -> &S>>;
268
269/// An iterator mutably borrowing only the values of a [`Punctuated`] sequence.
270pub type ValuesMut<'a, S, P> =
271    Chain<Once<&'a mut S>, Map<PairsMut<'a, S, P>, fn(&mut (P, S)) -> &mut S>>;
272
273/// An iterator owning punctuation-value pairs from a [`Punctuated`] sequence.
274pub type IntoPairs<S, P> = Chain<Once<(P, S)>, IntoIter<(P, S)>>;
275
276/// An iterator owning only the values of a [`Punctuated`] sequence.
277pub type IntoValues<S, P> = Chain<Once<S>, Map<IntoPairs<S, P>, fn((P, S)) -> S>>;
278
279impl<S, P> IntoIterator for Punctuated<S, P> {
280    type Item = S;
281    type IntoIter = IntoValues<S, P>;
282
283    fn into_iter(self) -> Self::IntoIter {
284        once(self.first).chain(
285            once(self.second)
286                .chain(self.rest)
287                .map(rest_value::<S, P> as fn((P, S)) -> S),
288        )
289    }
290}
291
292impl<'a, S, P> IntoIterator for &'a Punctuated<S, P> {
293    type Item = &'a S;
294    type IntoIter = Values<'a, S, P>;
295
296    fn into_iter(self) -> Self::IntoIter {
297        self.iter()
298    }
299}
300
301impl<'a, S, P> IntoIterator for &'a mut Punctuated<S, P> {
302    type Item = &'a mut S;
303    type IntoIter = ValuesMut<'a, S, P>;
304
305    fn into_iter(self) -> Self::IntoIter {
306        self.iter_mut()
307    }
308}
309
310/// Handles rest value.
311fn rest_value<S, P>((_, value): (P, S)) -> S {
312    value
313}
314
315/// Handles rest value ref.
316fn rest_value_ref<S, P>((_, value): &(P, S)) -> &S {
317    value
318}
319
320/// Handles rest value mut.
321fn rest_value_mut<S, P>((_, value): &mut (P, S)) -> &mut S {
322    value
323}
324
325#[cfg(test)]
326mod tests {
327    use super::*;
328
329    #[test]
330    fn a_sequence_is_nonempty_and_retains_separator_spans() {
331        let source = "+,+";
332        let values: Punctuated<Token![+], Token![,]> = crate::parse_fragment_str(source).unwrap();
333        let comma_span = values.pairs().next().unwrap().0.expect_span();
334
335        assert_eq!(values.len(), 2);
336        assert_eq!(values.token_len(), 3);
337        assert_eq!(comma_span.text(), Some(","));
338        assert_eq!(comma_span.bounds(), Some((1, 2)));
339        assert_eq!(values.to_m2(), "+,+");
340    }
341
342    #[test]
343    fn a_single_value_is_not_a_punctuated_sequence() {
344        let result = crate::parse_fragment_str::<Punctuated<Token![+]>>("+");
345
346        assert!(result.is_err());
347    }
348}