Skip to main content

m2_syn/parser/
punctuated.rs

1//! Nonempty syntax sequences separated by typed punctuation tokens.
2
3use std::iter::{Chain, Once, once};
4use std::{slice, vec};
5
6use crate::{Lex, Parse, ParseStream, Spanned, Token, TokenParseError, TokenStream};
7
8/// Two syntax values paired by one punctuation token.
9///
10/// Unlike [`Punctuated`], this type represents exactly one relation: a left
11/// value, its separator, and a right value. It is useful when a grammar field
12/// requires exactly two operands rather than an arbitrarily long sequence.
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Spanned)]
14pub struct PunctPair<L, P = Token![,], R = L> {
15    /// The value before the punctuation.
16    pub left: L,
17    /// The punctuation pairing the two values.
18    pub punctuation: P,
19    /// The value after the punctuation.
20    pub right: R,
21}
22
23impl<L, P, R> PunctPair<L, P, R> {
24    /// Creates a pair from its two values and their punctuation.
25    pub fn new(left: L, punctuation: P, right: R) -> Self {
26        Self {
27            left,
28            punctuation,
29            right,
30        }
31    }
32
33    /// Separates the pair into its two values and punctuation.
34    pub fn into_parts(self) -> (L, P, R) {
35        (self.left, self.punctuation, self.right)
36    }
37}
38
39impl<L: Parse, P: Parse, R: Parse> Parse for PunctPair<L, P, R> {
40    fn parse(input: &mut ParseStream) -> Result<Self, TokenParseError> {
41        Ok(Self::new(
42            L::parse(input)?,
43            P::parse(input)?,
44            R::parse(input)?,
45        ))
46    }
47}
48
49impl<L: Lex, P: Lex, R: Lex> Lex for PunctPair<L, P, R> {
50    fn to_tokens(&self, output: &mut TokenStream) {
51        self.left.to_tokens(output);
52        self.punctuation.to_tokens(output);
53        self.right.to_tokens(output);
54    }
55}
56
57/// Names or constructs a nonempty comma-punctuated sequence.
58///
59/// - `punc!(T)` names `Punctuated<T>`.
60/// - `punc!(value first)` constructs one value.
61/// - `punc!(pairs first; comma => second, ...)` constructs a sequence while
62///   retaining every supplied typed comma and its span.
63#[macro_export]
64macro_rules! punc {
65    (value $value:expr) => {
66        $crate::Punctuated::from_single($value)
67    };
68    (pairs $first:expr; $($punctuation:expr => $next:expr),+ $(,)?) => {{
69        let mut values = $crate::Punctuated::from_single($first);
70        $(values.push($punctuation, $next);)+
71        values
72    }};
73    ($value:ty) => {
74        $crate::Punctuated<$value>
75    };
76}
77
78/// A nonempty sequence of syntax values separated by typed punctuation.
79///
80/// Every separator is stored as its actual token, including its source span.
81/// The first value is mandatory; optional or empty grammar positions belong in
82/// the element type, such as `Option<T>` or a syntax enum containing an empty
83/// variant, while an optional whole sequence is `Option<Punctuated<T, P>>`.
84///
85/// ```
86/// use m2_syn::{Lex, Span};
87///
88/// type Pluses = m2_syn::punc!(m2_syn::Token![+]);
89/// let comma = m2_syn::Token![,](Span::for_text(","));
90/// let values: Pluses = m2_syn::punc!(
91///     pairs m2_syn::Token![+](Span::for_text("+"));
92///     comma => m2_syn::Token![+](Span::for_text("+")),
93/// );
94/// assert_eq!(values.to_m2(), "+,+");
95/// ```
96#[derive(Debug, Clone, PartialEq, Eq, Spanned)]
97pub struct Punctuated<S, P = Token![,]> {
98    first: S,
99    rest: Vec<(P, S)>,
100}
101
102impl<S, P> Punctuated<S, P> {
103    /// Creates a sequence from its first value and remaining punctuation-value
104    /// pairs.
105    pub fn new(first: S, rest: Vec<(P, S)>) -> Self {
106        Self { first, rest }
107    }
108
109    /// Creates a sequence containing exactly one value.
110    pub fn from_single(value: S) -> Self {
111        Self::new(value, Vec::new())
112    }
113
114    /// Creates a two-value sequence from one punctuated pair.
115    pub fn from_pair(pair: PunctPair<S, P>) -> Self {
116        let (left, punctuation, right) = pair.into_parts();
117        Self::new(left, vec![(punctuation, right)])
118    }
119
120    /// Returns the first value.
121    pub fn first(&self) -> &S {
122        &self.first
123    }
124
125    /// Returns the first value mutably.
126    pub fn first_mut(&mut self) -> &mut S {
127        &mut self.first
128    }
129
130    /// Returns the punctuation-value pairs following the first value.
131    pub fn rest(&self) -> &[(P, S)] {
132        &self.rest
133    }
134
135    /// Returns the punctuation-value pairs following the first value mutably.
136    pub fn rest_mut(&mut self) -> &mut [(P, S)] {
137        &mut self.rest
138    }
139
140    /// Separates the sequence into its first value and remaining pairs.
141    pub fn into_parts(self) -> (S, Vec<(P, S)>) {
142        (self.first, self.rest)
143    }
144
145    /// Returns the number of values, excluding punctuation.
146    pub fn len(&self) -> usize {
147        self.rest.len() + 1
148    }
149
150    /// Returns `false`; a punctuated sequence always contains its first value.
151    pub const fn is_empty(&self) -> bool {
152        false
153    }
154
155    /// Returns the number of values and punctuation tokens together.
156    pub fn token_len(&self) -> usize {
157        self.rest.len() * 2 + 1
158    }
159
160    /// Borrows the values without discarding or synthesizing punctuation.
161    pub fn iter(&self) -> Values<'_, S, P> {
162        once(&self.first).chain(
163            self.rest
164                .iter()
165                .map(rest_value_ref::<S, P> as fn(&(P, S)) -> &S),
166        )
167    }
168
169    /// Mutably borrows the values without discarding or synthesizing
170    /// punctuation.
171    pub fn iter_mut(&mut self) -> ValuesMut<'_, S, P> {
172        once(&mut self.first).chain(
173            self.rest
174                .iter_mut()
175                .map(rest_value_mut::<S, P> as fn(&mut (P, S)) -> &mut S),
176        )
177    }
178
179    /// Appends a real punctuation token and the value following it.
180    pub fn push(&mut self, punctuation: P, value: S) {
181        self.rest.push((punctuation, value));
182    }
183}
184
185impl<S: Lex, P: Lex> Lex for Punctuated<S, P> {
186    fn to_tokens(&self, output: &mut TokenStream) {
187        self.first.to_tokens(output);
188        for (punctuation, value) in &self.rest {
189            punctuation.to_tokens(output);
190            value.to_tokens(output);
191        }
192    }
193}
194
195impl<S, P> Parse for Punctuated<S, P>
196where
197    S: Parse,
198    P: Parse + Token,
199{
200    fn parse(input: &mut ParseStream) -> Result<Self, TokenParseError> {
201        let first = S::parse(input)?;
202        let mut values = Self::from_single(first);
203        while input.peek().is_some_and(P::matches_token_tree) {
204            let punctuation = P::parse(input)?;
205            values.push(punctuation, S::parse(input)?);
206        }
207        Ok(values)
208    }
209}
210
211/// An iterator borrowing only the values of a [`Punctuated`] sequence.
212pub type Values<'a, S, P> =
213    Chain<Once<&'a S>, std::iter::Map<slice::Iter<'a, (P, S)>, fn(&(P, S)) -> &S>>;
214
215/// An iterator mutably borrowing only the values of a [`Punctuated`] sequence.
216pub type ValuesMut<'a, S, P> =
217    Chain<Once<&'a mut S>, std::iter::Map<slice::IterMut<'a, (P, S)>, fn(&mut (P, S)) -> &mut S>>;
218
219/// An iterator owning only the values of a [`Punctuated`] sequence.
220pub type IntoValues<S, P> = Chain<Once<S>, std::iter::Map<vec::IntoIter<(P, S)>, fn((P, S)) -> S>>;
221
222impl<S, P> IntoIterator for Punctuated<S, P> {
223    type Item = S;
224    type IntoIter = IntoValues<S, P>;
225
226    fn into_iter(self) -> Self::IntoIter {
227        once(self.first).chain(
228            self.rest
229                .into_iter()
230                .map(rest_value::<S, P> as fn((P, S)) -> S),
231        )
232    }
233}
234
235impl<'a, S, P> IntoIterator for &'a Punctuated<S, P> {
236    type Item = &'a S;
237    type IntoIter = Values<'a, S, P>;
238
239    fn into_iter(self) -> Self::IntoIter {
240        self.iter()
241    }
242}
243
244impl<'a, S, P> IntoIterator for &'a mut Punctuated<S, P> {
245    type Item = &'a mut S;
246    type IntoIter = ValuesMut<'a, S, P>;
247
248    fn into_iter(self) -> Self::IntoIter {
249        self.iter_mut()
250    }
251}
252
253fn rest_value<S, P>((_, value): (P, S)) -> S {
254    value
255}
256
257fn rest_value_ref<S, P>((_, value): &(P, S)) -> &S {
258    value
259}
260
261fn rest_value_mut<S, P>((_, value): &mut (P, S)) -> &mut S {
262    value
263}
264
265#[cfg(test)]
266mod tests {
267    use super::*;
268
269    #[test]
270    fn a_sequence_is_nonempty_and_retains_separator_spans() {
271        let source = "+,+";
272        let values: Punctuated<Token![+], Token![,]> = crate::parse_fragment_str(source).unwrap();
273        let comma_span = values.rest()[0].0.expect_span();
274
275        assert_eq!(values.len(), 2);
276        assert_eq!(values.token_len(), 3);
277        assert_eq!(comma_span.text(), Some(","));
278        assert_eq!(comma_span.bounds(), Some((1, 2)));
279        assert_eq!(values.to_m2(), "+,+");
280    }
281}