time_macros/format_description/
ast.rs1use 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 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}