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