Skip to main content

m2_syn/
delimited.rs

1//! Typed syntax contained by one of the six delimiter families.
2
3use crate::{
4    CellStream, DelimiterKind, DelimiterToken, Parse, ParseStream, Span, Spanned, TextRange,
5    ToCells, ToTokens, TokenParseError, TokenStream, TokenTree, parse1,
6};
7
8/// Syntax contained by a statically known delimiter family.
9///
10/// `D` is normally named through [`crate::Delimiter!`], for example
11/// `Delimited<S, Delimiter![()]>`. The delimiter retains both boundary spans,
12/// while `contents` owns the syntax between them.
13///
14/// ```
15/// use m2_syn::{Span, ToTokens};
16///
17/// type ParenthesizedPlus = m2_syn::paren!(m2_syn::Token![+]);
18/// let value: ParenthesizedPlus = m2_syn::paren!(
19///     m2_syn::Token![+](Span::detached()),
20///     Span::detached(),
21/// );
22/// assert_eq!(value.to_m2(), "(+)");
23/// ```
24#[derive(Debug, Clone, PartialEq, Eq, Spanned)]
25pub struct Delimited<S, D> {
26    /// The typed delimiter family and its two boundary spans.
27    pub delimiter: D,
28    /// The syntax enclosed or terminated by the delimiter.
29    pub contents: S,
30}
31
32/// Names or constructs parenthesized syntax.
33///
34/// `paren!(T)` is `Delimited<T, Delimiter![()]>`. The constructor form is
35/// `paren!(value, span)` where `span` describes the complete parenthesized value.
36#[macro_export]
37macro_rules! paren {
38    ($contents:expr, $span:expr $(,)?) => {
39        $crate::Delimited::new($crate::Delimiter![()]($span), $contents)
40    };
41    ($contents:ty) => {
42        $crate::Delimited<$contents, $crate::Delimiter![()]>
43    };
44}
45
46/// Names or constructs brace-delimited syntax.
47///
48/// Use `braces!(T)` as a type and `braces!(value, spans)` as a constructor.
49#[macro_export]
50macro_rules! braces {
51    ($contents:expr, $span:expr $(,)?) => {
52        $crate::Delimited::new($crate::Delimiter![{}]($span), $contents)
53    };
54    ($contents:ty) => {
55        $crate::Delimited<$contents, $crate::Delimiter![{}]>
56    };
57}
58
59/// Names or constructs bracket-delimited syntax.
60///
61/// Use `brackets!(T)` as a type and `brackets!(value, spans)` as a constructor.
62#[macro_export]
63macro_rules! brackets {
64    ($contents:expr, $span:expr $(,)?) => {
65        $crate::Delimited::new($crate::Delimiter![[]]($span), $contents)
66    };
67    ($contents:ty) => {
68        $crate::Delimited<$contents, $crate::Delimiter![[]]>
69    };
70}
71
72/// Names or constructs angle-bar-delimited syntax.
73///
74/// Use `angle_bars!(T)` as a type and `angle_bars!(value, spans)` as a
75/// constructor.
76#[macro_export]
77macro_rules! angle_bars {
78    ($contents:expr, $span:expr $(,)?) => {
79        $crate::Delimited::new($crate::Delimiter![<| |>]($span), $contents)
80    };
81    ($contents:ty) => {
82        $crate::Delimited<$contents, $crate::Delimiter![<| |>]>
83    };
84}
85
86/// Names or constructs syntax with an implicit empty delimiter.
87///
88/// Use `naked!(T)` as a type and `naked!(value, spans)` as a constructor.
89#[macro_export]
90macro_rules! naked {
91    ($contents:expr, $span:expr $(,)?) => {
92        $crate::Delimited::new($crate::Delimiter![]($span), $contents)
93    };
94    ($contents:ty) => {
95        $crate::Delimited<$contents, $crate::Delimiter![]>
96    };
97}
98
99/// Names or constructs syntax terminated by a semicolon delimiter.
100///
101/// Use `semicolon!(T)` as a type and `semicolon!(value, spans)` as a
102/// constructor.
103#[macro_export]
104macro_rules! semicolon {
105    ($contents:expr, $span:expr $(,)?) => {
106        $crate::Delimited::new($crate::Delimiter![;]($span), $contents)
107    };
108    ($contents:ty) => {
109        $crate::Delimited<$contents, $crate::Delimiter![;]>
110    };
111}
112
113impl<S, D> Delimited<S, D> {
114    /// Creates a delimited value from its delimiter and contents.
115    pub fn new(delimiter: D, contents: S) -> Self {
116        Self {
117            delimiter,
118            contents,
119        }
120    }
121
122    /// Separates the delimiter from the contained syntax.
123    pub fn into_parts(self) -> (D, S) {
124        (self.delimiter, self.contents)
125    }
126}
127
128impl<S, D> Parse for Delimited<S, D>
129where
130    S: Parse,
131    D: DelimiterToken,
132{
133    fn parse(input: &mut ParseStream) -> Result<Self, TokenParseError> {
134        if D::KIND == DelimiterKind::Empty {
135            let opening = input
136                .peek()
137                .map(|token| boundary_before(token.span()))
138                .unwrap_or_else(|| input.eof_span());
139            let contents = S::parse(input)?;
140            let delimiter = D::new(opening.join(input.eof_span()));
141            return Ok(Self::new(delimiter, contents));
142        }
143
144        if D::KIND == DelimiterKind::Semicolon {
145            if input
146                .peek()
147                .is_some_and(<Token![;] as crate::Token>::matches_token_tree)
148            {
149                let semicolon = input.peek().unwrap();
150                return Err(TokenParseError::UnexpectedToken {
151                    expected: "nonempty syntax before `;`",
152                    found: ";".to_owned(),
153                    span: semicolon.span(),
154                });
155            }
156            let opening = input
157                .peek()
158                .map(|token| boundary_before(token.span()))
159                .unwrap_or_else(|| input.eof_span());
160            let contents = S::parse(input)?;
161            let semicolon = <Token![;]>::parse(input)?;
162            let delimiter = D::new(opening.join(semicolon.span()));
163            return Ok(Self::new(delimiter, contents));
164        }
165
166        let Some(token) = input.next_token() else {
167            return Err(TokenParseError::UnexpectedEnd {
168                expected: D::KIND.opening(),
169                span: input.eof_span(),
170            });
171        };
172        let span = token.span();
173        let TokenTree::Group(group) = token else {
174            return Err(TokenParseError::UnexpectedToken {
175                expected: D::KIND.opening(),
176                found: token.spelling().unwrap_or("trivia").to_owned(),
177                span,
178            });
179        };
180        if group.delim_kind() != D::KIND {
181            return Err(TokenParseError::UnexpectedToken {
182                expected: D::KIND.opening(),
183                found: group.delim_kind().to_string(),
184                span,
185            });
186        }
187
188        let delimiter = D::new(group.span());
189        let contents = parse1(group.into_stream())?;
190        Ok(Self::new(delimiter, contents))
191    }
192}
193
194fn boundary_before(span: Span) -> Span {
195    span.source()
196        .ok()
197        .zip(span.start_point().ok())
198        .map(|(source, point)| Span::new(source, TextRange::from_point(point)))
199        .unwrap_or_else(Span::detached)
200}
201
202impl<S, D> ToTokens for Delimited<S, D>
203where
204    S: ToTokens,
205    D: DelimiterToken,
206{
207    fn to_tokens(&self, output: &mut TokenStream) {
208        self.delimiter
209            .surround(output, self.contents.to_token_stream());
210    }
211}
212
213impl<S, D> ToCells for Delimited<S, D>
214where
215    S: ToCells,
216{
217    fn to_cells(&self, output: &mut CellStream) {
218        self.contents.to_cells(output);
219    }
220}