Skip to main content

m2_syn/token_stream/
delim.rs

1//! Raw and typed delimiter support.
2//!
3//! [`Delimiter`] is stored by raw [`Group`] token trees. Generated
4//! `Delimiter![..]` atoms implement [`DelimiterToken`] and are stored on typed
5//! delimited nodes, so flattening can recover the corresponding raw group.
6
7use std::fmt::{Display, Formatter};
8
9use crate::{
10    Group, Parse, ParseStream, Punct, Span, Spanned, TextPoint, TextRange, ToTokens,
11    TokenParseError, TokenStream, TokenTree,
12};
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
15/// The six delimiter families recognized by raw syntax containers.
16///
17/// Paired delimiters surround ordinary token-tree groups. [`Empty`](Self::Empty)
18/// and [`Semicolon`](Self::Semicolon) delimit source cells: both have an
19/// implicit opening boundary, while only the semicolon has visible closing
20/// text.
21pub enum DelimiterKind {
22    Empty,
23    Semicolon,
24    Parenthesis,
25    Bracket,
26    Brace,
27    AngleBar,
28}
29
30/// A generated typed refinement of one raw delimiter kind.
31///
32/// Delimiter atoms are the container analogue of `Token![..]`: the typed CST
33/// stores the precise delimiter family, while [`Group`] and
34/// [`crate::CellBlock`] carry the erased raw representation.
35pub trait DelimiterToken: Parse + Spanned + ToTokens + Sized {
36    const KIND: DelimiterKind;
37
38    fn new(span: Span) -> Self;
39
40    fn surround(&self, output: &mut TokenStream, contents: TokenStream) {
41        Delimiter::new(Self::KIND, self.span()).surround(output, contents);
42    }
43}
44
45#[derive(Debug, Clone, Copy, PartialEq, Eq, Spanned)]
46/// Type-erased delimiter data stored by a raw [`Group`].
47pub struct Delimiter {
48    pub kind: DelimiterKind,
49    /// The complete span of the delimited container.
50    pub span: Span,
51}
52
53impl Delimiter {
54    pub fn new(kind: DelimiterKind, span: Span) -> Self {
55        Self { kind, span }
56    }
57
58    /// Returns the opening delimiter span derived from the container span.
59    pub fn opening_span(self) -> Span {
60        boundary_span(self.span, Boundary::Opening, self.kind.opening().len())
61    }
62
63    /// Returns the closing delimiter span derived from the container span.
64    pub fn closing_span(self) -> Span {
65        boundary_span(self.span, Boundary::Closing, self.kind.closing().len())
66    }
67
68    /// Emits `contents` with this delimiter's concrete token-tree shape.
69    pub fn surround(&self, output: &mut TokenStream, contents: TokenStream) {
70        match self.kind {
71            DelimiterKind::Empty => output.extend([contents]),
72            DelimiterKind::Semicolon => {
73                output.extend([contents]);
74                output.push_punct(Punct::new(";", self.closing_span()));
75            }
76            DelimiterKind::Parenthesis
77            | DelimiterKind::Bracket
78            | DelimiterKind::Brace
79            | DelimiterKind::AngleBar => output.push_group(Group::new(*self, contents)),
80        }
81    }
82}
83
84/// Parses one delimiter atom without any contained syntax.
85#[doc(hidden)]
86pub fn parse_delimiter<D: DelimiterToken>(input: &mut ParseStream) -> Result<D, TokenParseError> {
87    match D::KIND {
88        DelimiterKind::Empty => {
89            let span = input
90                .peek()
91                .map(|token| boundary_before(token.span()))
92                .unwrap_or_else(|| input.eof_span());
93            Ok(D::new(span))
94        }
95        DelimiterKind::Semicolon => {
96            let semicolon = <Token![;]>::parse(input)?;
97            Ok(D::new(semicolon.span()))
98        }
99        kind => {
100            let Some(token) = input.next_token() else {
101                return Err(TokenParseError::UnexpectedEnd {
102                    expected: kind.opening(),
103                    span: input.eof_span(),
104                });
105            };
106            let span = token.span();
107            let TokenTree::Group(group) = token else {
108                return Err(TokenParseError::UnexpectedToken {
109                    expected: kind.opening(),
110                    found: token.spelling().unwrap_or("trivia").to_owned(),
111                    span,
112                });
113            };
114            if group.delim_kind() != kind || !group.stream().is_empty() {
115                return Err(TokenParseError::UnexpectedToken {
116                    expected: kind.opening(),
117                    found: group.delim_kind().opening().to_owned(),
118                    span,
119                });
120            }
121            Ok(D::new(group.span()))
122        }
123    }
124}
125
126impl Display for Delimiter {
127    fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
128        formatter.write_str(self.kind.opening())
129    }
130}
131impl Display for DelimiterKind {
132    fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
133        formatter.write_str(self.opening())
134    }
135}
136
137impl DelimiterKind {
138    pub fn opening(self) -> &'static str {
139        match self {
140            Self::Empty | Self::Semicolon => "",
141            Self::Parenthesis => "(",
142            Self::Bracket => "[",
143            Self::Brace => "{",
144            Self::AngleBar => "<|",
145        }
146    }
147
148    pub fn closing(self) -> &'static str {
149        match self {
150            Self::Empty => "",
151            Self::Semicolon => ";",
152            Self::Parenthesis => ")",
153            Self::Bracket => "]",
154            Self::Brace => "}",
155            Self::AngleBar => "|>",
156        }
157    }
158}
159
160#[derive(Clone, Copy)]
161enum Boundary {
162    Opening,
163    Closing,
164}
165
166fn boundary_span(span: Span, boundary: Boundary, width: usize) -> Span {
167    let Ok(range) = span.range() else {
168        return Span::detached();
169    };
170    let (Some(start), Some(end)) = (range.start(), range.end()) else {
171        return Span::detached();
172    };
173    if end.byte.saturating_sub(start.byte) < width {
174        return span;
175    }
176
177    let range = match boundary {
178        Boundary::Opening => TextRange::new(start, shifted(start, width)),
179        Boundary::Closing => TextRange::new(shifted_back(end, width), end),
180    };
181    match span {
182        Span::FileLocated { source, .. } => Span::new(source, range),
183        Span::LocalRefFrame { .. } => Span::in_tmp_file(range),
184        Span::Detached => Span::detached(),
185    }
186}
187
188fn shifted(mut point: TextPoint, width: usize) -> TextPoint {
189    point.byte = point.byte.saturating_add(width);
190    point.column = point.column.saturating_add(width as u32);
191    point
192}
193
194fn shifted_back(mut point: TextPoint, width: usize) -> TextPoint {
195    point.byte = point.byte.saturating_sub(width);
196    point.column = point.column.saturating_sub(width as u32);
197    point
198}
199
200fn boundary_before(span: Span) -> Span {
201    span.source()
202        .ok()
203        .zip(span.start_point().ok())
204        .map(|(source, point)| Span::new(source, TextRange::from_point(point)))
205        .unwrap_or_else(Span::detached)
206}