Skip to main content

time_macros/format_description/
ast.rs

1use std::iter;
2
3use super::{Error, Location, Spanned, SpannedValue, Unused, lexer, unused};
4use crate::FormatDescriptionVersion;
5use crate::format_description::Span;
6
7pub(super) enum Item<'a> {
8    Literal {
9        version: FormatDescriptionVersion,
10        value: Spanned<&'a [u8]>,
11    },
12    EscapedBracket {
13        _first: Unused<Location>,
14        _second: Unused<Location>,
15    },
16    Component {
17        _opening_bracket: Unused<Location>,
18        _leading_whitespace: Unused<Option<Spanned<&'a [u8]>>>,
19        name: Spanned<&'a [u8]>,
20        modifiers: Box<[Modifier<'a>]>,
21        _trailing_whitespace: Unused<Option<Spanned<&'a [u8]>>>,
22        _closing_bracket: Unused<Location>,
23    },
24    Optional {
25        version: FormatDescriptionVersion,
26        opening_bracket: Location,
27        _leading_whitespace: Unused<Option<Spanned<&'a [u8]>>>,
28        _optional_kw: Unused<Spanned<&'a [u8]>>,
29        modifiers: Box<[Modifier<'a>]>,
30        _whitespace_after_modifiers: Unused<Option<Spanned<&'a [u8]>>>,
31        nested_format_description: NestedFormatDescription<'a>,
32        closing_bracket: Location,
33    },
34    First {
35        opening_bracket: Location,
36        _leading_whitespace: Unused<Option<Spanned<&'a [u8]>>>,
37        _first_kw: Unused<Spanned<&'a [u8]>>,
38        modifiers: Box<[Modifier<'a>]>,
39        _whitespace_after_modifiers: Unused<Option<Spanned<&'a [u8]>>>,
40        nested_format_descriptions: Box<[NestedFormatDescription<'a>]>,
41        closing_bracket: Location,
42    },
43}
44
45pub(super) struct NestedFormatDescription<'a> {
46    pub(super) _opening_bracket: Unused<Location>,
47    pub(super) items: Box<[Item<'a>]>,
48    pub(super) _closing_bracket: Unused<Location>,
49    pub(super) _trailing_whitespace: Unused<Option<Spanned<&'a [u8]>>>,
50}
51
52#[derive(Debug)]
53pub(super) struct Modifier<'a> {
54    pub(super) _leading_whitespace: Unused<Spanned<&'a [u8]>>,
55    pub(super) key: Spanned<&'a [u8]>,
56    pub(super) _colon: Unused<Location>,
57    pub(super) value: Spanned<&'a [u8]>,
58}
59
60impl<'a> Modifier<'a> {
61    fn from_leading_whitespace_and_token(
62        leading_whitespace: Spanned<&'a [u8]>,
63        token: Spanned<&'a [u8]>,
64    ) -> Result<Self, Error> {
65        let Some(colon_index) = token.iter().position(|&b| b == b':') else {
66            return Err(token.span.error("modifier must be of the form `key:value`"));
67        };
68        let key = &token[..colon_index];
69        let value = &token[colon_index + 1..];
70
71        if key.is_empty() {
72            return Err(token.span.shrink_to_start().error("expected modifier key"));
73        }
74        if value.is_empty() {
75            return Err(token.span.shrink_to_end().error("expected modifier value"));
76        }
77
78        Ok(Self {
79            _leading_whitespace: unused(leading_whitespace),
80            key: key.spanned(token.span),
81            _colon: unused(token.span.start.offset(colon_index as u32)),
82            value: value.spanned(token.span),
83        })
84    }
85
86    pub(super) fn key_value_span(&self) -> Span {
87        self.key.span.start.to(self.value.span.end)
88    }
89}
90
91pub(super) fn parse<'item: 'iter, 'iter, I: Iterator<Item = Result<lexer::Token<'item>, Error>>>(
92    version: FormatDescriptionVersion,
93    tokens: &'iter mut lexer::Lexed<I>,
94) -> impl Iterator<Item = Result<Item<'item>, Error>> + use<'item, 'iter, I> {
95    parse_inner(version, false, tokens)
96}
97
98fn parse_inner<'item, I: Iterator<Item = Result<lexer::Token<'item>, Error>>>(
99    version: FormatDescriptionVersion,
100    nested: bool,
101    tokens: &mut lexer::Lexed<I>,
102) -> impl Iterator<Item = Result<Item<'item>, Error>> + use<'_, 'item, I> {
103    iter::from_fn(move || {
104        if nested && tokens.peek_closing_bracket().is_some() {
105            return None;
106        }
107
108        let next = match tokens.next()? {
109            Ok(token) => token,
110            Err(err) => return Some(Err(err)),
111        };
112
113        Some(match next {
114            lexer::Token::Literal(Spanned { value: _, span: _ }) if nested => {
115                bug!("literal should not be present in nested description")
116            }
117            lexer::Token::Literal(value) => Ok(Item::Literal { version, value }),
118            lexer::Token::Bracket {
119                kind: lexer::BracketKind::Opening,
120                location,
121            } => {
122                if version.is_v1()
123                    && let Some(second_location) = tokens.next_if_opening_bracket()
124                {
125                    Ok(Item::EscapedBracket {
126                        _first: unused(location),
127                        _second: unused(second_location),
128                    })
129                } else {
130                    parse_component(version, location, tokens)
131                }
132            }
133            lexer::Token::Bracket {
134                kind: lexer::BracketKind::Closing,
135                location: _,
136            } if nested => {
137                bug!("closing bracket should be caught by the `if` statement")
138            }
139            lexer::Token::Bracket {
140                kind: lexer::BracketKind::Closing,
141                location: _,
142            } => {
143                bug!("closing bracket should have been consumed by `parse_component`")
144            }
145            lexer::Token::ComponentPart { kind: _, value } if nested => {
146                Ok(Item::Literal { version, value })
147            }
148            lexer::Token::ComponentPart { kind: _, value: _ } => {
149                bug!("component part should have been consumed by `parse_component`")
150            }
151        })
152    })
153}
154
155struct Modifiers<'a> {
156    modifiers: Box<[Modifier<'a>]>,
157    trailing_whitespace: Option<Spanned<&'a [u8]>>,
158}
159
160impl<'a> Modifiers<'a> {
161    fn parse<I>(nested_is_allowed: bool, tokens: &mut lexer::Lexed<I>) -> Result<Self, Error>
162    where
163        I: Iterator<Item = Result<lexer::Token<'a>, Error>>,
164    {
165        let mut modifiers = Vec::new();
166        loop {
167            let Some(whitespace) = tokens.next_if_whitespace() else {
168                return Ok(Self {
169                    modifiers: modifiers.into_boxed_slice(),
170                    trailing_whitespace: None,
171                });
172            };
173
174            // This is not necessary for proper parsing, but provides a much better error when a
175            // nested description is used where it's not allowed.
176            if !nested_is_allowed && let Some(location) = tokens.next_if_opening_bracket() {
177                return Err(location.error("modifier must be of the form `key:value`"));
178            }
179
180            let Some(token) = tokens.next_if_not_whitespace() else {
181                return Ok(Self {
182                    modifiers: modifiers.into_boxed_slice(),
183                    trailing_whitespace: Some(whitespace),
184                });
185            };
186
187            let modifier = Modifier::from_leading_whitespace_and_token(whitespace, token)?;
188            modifiers.push(modifier);
189        }
190    }
191
192    fn span(&self) -> Span {
193        match &*self.modifiers {
194            [] => self
195                .trailing_whitespace
196                .map(|whitespace| whitespace.span)
197                .unwrap_or_else(Span::dummy),
198            [modifier] => modifier.key.span.start.to(modifier.value.span.end),
199            [first, .., last] => first.key.span.start.to(last.value.span.end),
200        }
201    }
202}
203
204fn parse_component<'a, I: Iterator<Item = Result<lexer::Token<'a>, Error>>>(
205    version: FormatDescriptionVersion,
206    opening_bracket: Location,
207    tokens: &mut lexer::Lexed<I>,
208) -> Result<Item<'a>, Error> {
209    let leading_whitespace = tokens.next_if_whitespace();
210
211    let Some(name) = tokens.next_if_not_whitespace() else {
212        let span = match leading_whitespace {
213            Some(Spanned { value: _, span }) => span,
214            None => opening_bracket.to(opening_bracket),
215        };
216        return Err(span.error("expected component name"));
217    };
218
219    if *name == b"optional" {
220        let modifiers = Modifiers::parse(true, tokens)?;
221        let nested = parse_nested(version, modifiers.span().end, tokens)?;
222
223        let Some(closing_bracket) = tokens.next_if_closing_bracket() else {
224            return Err(opening_bracket.error("unclosed bracket"));
225        };
226
227        if modifiers.trailing_whitespace.is_none() {
228            if let Some(modifier) = modifiers.modifiers.last() {
229                return Err(modifier
230                    .value
231                    .span
232                    .shrink_to_end()
233                    .error("expected whitespace between modifiers and nested description"));
234            } else {
235                return Err(name
236                    .span
237                    .shrink_to_end()
238                    .error("expected whitespace between `optional` and nested description"));
239            }
240        }
241
242        return Ok(Item::Optional {
243            version,
244            opening_bracket,
245            _leading_whitespace: unused(leading_whitespace),
246            _optional_kw: unused(name),
247            modifiers: modifiers.modifiers,
248            _whitespace_after_modifiers: unused(modifiers.trailing_whitespace),
249            nested_format_description: nested,
250            closing_bracket,
251        });
252    }
253
254    if *name == b"first" {
255        let modifiers = Modifiers::parse(true, tokens)?;
256
257        let mut nested_format_descriptions = Vec::new();
258        while let Ok(description) = parse_nested(version, modifiers.span().end, tokens) {
259            nested_format_descriptions.push(description);
260        }
261
262        if version.is_at_least_v3() && nested_format_descriptions.is_empty() {
263            return Err(modifiers
264                .span()
265                .shrink_to_end()
266                .error("expected at least one nested description"));
267        }
268
269        let Some(closing_bracket) = tokens.next_if_closing_bracket() else {
270            return Err(opening_bracket.error("unclosed bracket"));
271        };
272
273        if modifiers.trailing_whitespace.is_none() {
274            if let Some(modifier) = modifiers.modifiers.last() {
275                return Err(modifier
276                    .value
277                    .span
278                    .shrink_to_end()
279                    .error("expected whitespace between modifiers and nested descriptions"));
280            } else {
281                return Err(name
282                    .span
283                    .shrink_to_end()
284                    .error("expected whitespace between `first` and nested descriptions"));
285            }
286        }
287
288        return Ok(Item::First {
289            opening_bracket,
290            _leading_whitespace: unused(leading_whitespace),
291            _first_kw: unused(name),
292            modifiers: modifiers.modifiers,
293            _whitespace_after_modifiers: unused(modifiers.trailing_whitespace),
294            nested_format_descriptions: nested_format_descriptions.into_boxed_slice(),
295            closing_bracket,
296        });
297    }
298
299    let Modifiers {
300        modifiers,
301        trailing_whitespace,
302    } = Modifiers::parse(false, tokens)?;
303
304    let Some(closing_bracket) = tokens.next_if_closing_bracket() else {
305        return Err(opening_bracket.error("unclosed bracket"));
306    };
307
308    Ok(Item::Component {
309        _opening_bracket: unused(opening_bracket),
310        _leading_whitespace: unused(leading_whitespace),
311        name,
312        modifiers,
313        _trailing_whitespace: unused(trailing_whitespace),
314        _closing_bracket: unused(closing_bracket),
315    })
316}
317
318fn parse_nested<'a, I: Iterator<Item = Result<lexer::Token<'a>, Error>>>(
319    version: FormatDescriptionVersion,
320    last_location: Location,
321    tokens: &mut lexer::Lexed<I>,
322) -> Result<NestedFormatDescription<'a>, Error> {
323    let Some(opening_bracket) = tokens.next_if_opening_bracket() else {
324        return Err(last_location.error("expected opening bracket"));
325    };
326    let items = parse_inner(version, true, tokens).collect::<Result<_, _>>()?;
327    let Some(closing_bracket) = tokens.next_if_closing_bracket() else {
328        return Err(opening_bracket.error("unclosed bracket"));
329    };
330    let trailing_whitespace = tokens.next_if_whitespace();
331
332    Ok(NestedFormatDescription {
333        _opening_bracket: unused(opening_bracket),
334        items,
335        _closing_bracket: unused(closing_bracket),
336        _trailing_whitespace: unused(trailing_whitespace),
337    })
338}