1 //! Abstract Syntax Tree representation of the Fluent Translation List.
2 //!
3 //! The AST of Fluent contains all nodes structures to represent a complete
4 //! representation of the FTL resource.
5 //!
6 //! The tree preserves all semantic information and allow for round-trip
7 //! of a canonically written FTL resource.
8 //!
9 //! The root node is called [`Resource`] and contains a list of [`Entry`] nodes
10 //! representing all possible entries in the Fluent Translation List.
11 //!
12 //! # Example
13 //!
14 //! ```
15 //! use fluent_syntax::parser;
16 //! use fluent_syntax::ast;
17 //!
18 //! let ftl = r#"
19 //!
20 //! ## This is a message comment
21 //! hello-world = Hello World!
22 //!     .tooltip = Tooltip for you, { $userName }.
23 //!
24 //! "#;
25 //!
26 //! let resource = parser::parse(ftl)
27 //!     .expect("Failed to parse an FTL resource.");
28 //!
29 //! assert_eq!(
30 //!     resource.body[0],
31 //!     ast::Entry::Message(
32 //!         ast::Message {
33 //!             id: ast::Identifier {
34 //!                 name: "hello-world"
35 //!             },
36 //!             value: Some(ast::Pattern {
37 //!                 elements: vec![
38 //!                     ast::PatternElement::TextElement {
39 //!                         value: "Hello World!"
40 //!                     },
41 //!                 ]
42 //!             }),
43 //!             attributes: vec![
44 //!                 ast::Attribute {
45 //!                     id: ast::Identifier {
46 //!                         name: "tooltip"
47 //!                     },
48 //!                     value: ast::Pattern {
49 //!                         elements: vec![
50 //!                             ast::PatternElement::TextElement {
51 //!                                 value: "Tooltip for you, "
52 //!                             },
53 //!                             ast::PatternElement::Placeable {
54 //!                                 expression: ast::Expression::Inline(
55 //!                                     ast::InlineExpression::VariableReference {
56 //!                                         id: ast::Identifier {
57 //!                                             name: "userName"
58 //!                                         }
59 //!                                     }
60 //!                                 )
61 //!                             },
62 //!                             ast::PatternElement::TextElement {
63 //!                                 value: "."
64 //!                             },
65 //!                         ]
66 //!                     }
67 //!                 }
68 //!             ],
69 //!             comment: Some(
70 //!                 ast::Comment {
71 //!                     content: vec!["This is a message comment"]
72 //!                 }
73 //!             )
74 //!         }
75 //!     ),
76 //! );
77 //! ```
78 //!
79 //! ## Errors
80 //!
81 //! Fluent AST preserves blocks containing invaid syntax as [`Entry::Junk`].
82 //!
83 //! ## White space
84 //!
85 //! At the moment, AST does not preserve white space. In result only a
86 //! canonical form of the AST is suitable for a round-trip.
87 mod helper;
88 
89 #[cfg(feature = "serde")]
90 use serde::{Deserialize, Serialize};
91 
92 /// Root node of a Fluent Translation List.
93 ///
94 /// A [`Resource`] contains a body with a list of [`Entry`] nodes.
95 ///
96 /// # Example
97 ///
98 /// ```
99 /// use fluent_syntax::parser;
100 /// use fluent_syntax::ast;
101 ///
102 /// let ftl = "";
103 ///
104 /// let resource = parser::parse(ftl)
105 ///     .expect("Failed to parse an FTL resource.");
106 ///
107 /// assert_eq!(
108 ///     resource,
109 ///     ast::Resource {
110 ///         body: vec![]
111 ///     }
112 /// );
113 /// ```
114 #[derive(Debug, PartialEq, Clone)]
115 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
116 pub struct Resource<S> {
117     pub body: Vec<Entry<S>>,
118 }
119 
120 /// A top-level node representing an entry of a [`Resource`].
121 ///
122 /// Every [`Entry`] is a standalone element and the parser is capable
123 /// of recovering from errors by identifying a beginning of a next entry.
124 ///
125 /// # Example
126 ///
127 /// ```
128 /// use fluent_syntax::parser;
129 /// use fluent_syntax::ast;
130 ///
131 /// let ftl = r#"
132 ///
133 /// key = Value
134 ///
135 /// "#;
136 ///
137 /// let resource = parser::parse(ftl)
138 ///     .expect("Failed to parse an FTL resource.");
139 ///
140 /// assert_eq!(
141 ///     resource,
142 ///     ast::Resource {
143 ///         body: vec![
144 ///             ast::Entry::Message(
145 ///                 ast::Message {
146 ///                     id: ast::Identifier {
147 ///                         name: "key"
148 ///                     },
149 ///                     value: Some(ast::Pattern {
150 ///                         elements: vec![
151 ///                             ast::PatternElement::TextElement {
152 ///                                 value: "Value"
153 ///                             },
154 ///                         ]
155 ///                     }),
156 ///                     attributes: vec![],
157 ///                     comment: None,
158 ///                 }
159 ///             )
160 ///         ]
161 ///     }
162 /// );
163 /// ```
164 ///
165 /// # Junk Entry
166 ///
167 /// If FTL source contains invalid FTL content, it will be preserved
168 /// in form of [`Entry::Junk`] nodes.
169 ///
170 /// # Example
171 ///
172 /// ```
173 /// use fluent_syntax::parser;
174 /// use fluent_syntax::ast;
175 ///
176 /// let ftl = r#"
177 ///
178 /// g@rb@ge En!ry
179 ///
180 /// "#;
181 ///
182 /// let (resource, _) = parser::parse(ftl)
183 ///     .expect_err("Failed to parse an FTL resource.");
184 ///
185 /// assert_eq!(
186 ///     resource,
187 ///     ast::Resource {
188 ///         body: vec![
189 ///             ast::Entry::Junk {
190 ///                 content: "g@rb@ge En!ry\n\n"
191 ///             }
192 ///         ]
193 ///     }
194 /// );
195 /// ```
196 #[derive(Debug, PartialEq, Clone)]
197 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
198 #[cfg_attr(feature = "serde", serde(tag = "type"))]
199 pub enum Entry<S> {
200     Message(Message<S>),
201     Term(Term<S>),
202     Comment(Comment<S>),
203     GroupComment(Comment<S>),
204     ResourceComment(Comment<S>),
205     Junk { content: S },
206 }
207 
208 /// Message node represents the most common [`Entry`] in an FTL [`Resource`].
209 ///
210 /// A message is a localization unit with a [`Identifier`] unique within a given
211 /// [`Resource`], and a value or attributes with associated [`Pattern`].
212 ///
213 /// A message can contain a simple text value, or a compound combination of value
214 /// and attributes which together can be used to localize a complex User Interface
215 /// element.
216 ///
217 /// Finally, each [`Message`] may have an associated [`Comment`].
218 ///
219 /// # Example
220 ///
221 /// ```
222 /// use fluent_syntax::parser;
223 /// use fluent_syntax::ast;
224 ///
225 /// let ftl = r#"
226 ///
227 /// hello-world = Hello, World!
228 ///
229 /// "#;
230 ///
231 /// let resource = parser::parse(ftl)
232 ///     .expect("Failed to parse an FTL resource.");
233 ///
234 /// assert_eq!(
235 ///     resource,
236 ///     ast::Resource {
237 ///         body: vec![
238 ///             ast::Entry::Message(ast::Message {
239 ///                 id: ast::Identifier {
240 ///                     name: "hello-world"
241 ///                 },
242 ///                 value: Some(ast::Pattern {
243 ///                     elements: vec![
244 ///                         ast::PatternElement::TextElement {
245 ///                             value: "Hello, World!"
246 ///                         }
247 ///                     ]
248 ///                 }),
249 ///                 attributes: vec![],
250 ///                 comment: None,
251 ///             })
252 ///         ]
253 ///     }
254 /// );
255 /// ```
256 #[derive(Debug, PartialEq, Clone)]
257 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
258 pub struct Message<S> {
259     pub id: Identifier<S>,
260     pub value: Option<Pattern<S>>,
261     pub attributes: Vec<Attribute<S>>,
262     pub comment: Option<Comment<S>>,
263 }
264 
265 /// A Fluent [`Term`].
266 ///
267 /// Terms are semantically similar to [`Message`] nodes, but
268 /// they represent a separate concept in Fluent system.
269 ///
270 /// Every term has to have a value, and the parser will
271 /// report errors when term references are used in wrong positions.
272 ///
273 /// # Example
274 ///
275 /// ```
276 /// use fluent_syntax::parser;
277 /// use fluent_syntax::ast;
278 ///
279 /// let ftl = r#"
280 ///
281 /// -brand-name = Nightly
282 ///
283 /// "#;
284 ///
285 /// let resource = parser::parse(ftl)
286 ///     .expect("Failed to parse an FTL resource.");
287 ///
288 /// assert_eq!(
289 ///     resource,
290 ///     ast::Resource {
291 ///         body: vec![
292 ///             ast::Entry::Term(ast::Term {
293 ///                 id: ast::Identifier {
294 ///                     name: "brand-name"
295 ///                 },
296 ///                 value: ast::Pattern {
297 ///                     elements: vec![
298 ///                         ast::PatternElement::TextElement {
299 ///                             value: "Nightly"
300 ///                         }
301 ///                     ]
302 ///                 },
303 ///                 attributes: vec![],
304 ///                 comment: None,
305 ///             })
306 ///         ]
307 ///     }
308 /// );
309 /// ```
310 #[derive(Debug, PartialEq, Clone)]
311 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
312 pub struct Term<S> {
313     pub id: Identifier<S>,
314     pub value: Pattern<S>,
315     pub attributes: Vec<Attribute<S>>,
316     pub comment: Option<Comment<S>>,
317 }
318 
319 /// Pattern contains a value of a [`Message`], [`Term`] or an [`Attribute`].
320 ///
321 /// Each pattern is a list of [`PatternElement`] nodes representing
322 /// either a simple textual value, or a combination of text literals
323 /// and placeholder [`Expression`] nodes.
324 ///
325 /// # Example
326 ///
327 /// ```
328 /// use fluent_syntax::parser;
329 /// use fluent_syntax::ast;
330 ///
331 /// let ftl = r#"
332 ///
333 /// hello-world = Hello, World!
334 ///
335 /// welcome = Welcome, { $userName }.
336 ///
337 /// "#;
338 ///
339 /// let resource = parser::parse(ftl)
340 ///     .expect("Failed to parse an FTL resource.");
341 ///
342 /// assert_eq!(
343 ///     resource,
344 ///     ast::Resource {
345 ///         body: vec![
346 ///             ast::Entry::Message(ast::Message {
347 ///                 id: ast::Identifier {
348 ///                     name: "hello-world"
349 ///                 },
350 ///                 value: Some(ast::Pattern {
351 ///                     elements: vec![
352 ///                         ast::PatternElement::TextElement {
353 ///                             value: "Hello, World!"
354 ///                         }
355 ///                     ]
356 ///                 }),
357 ///                 attributes: vec![],
358 ///                 comment: None,
359 ///             }),
360 ///             ast::Entry::Message(ast::Message {
361 ///                 id: ast::Identifier {
362 ///                     name: "welcome"
363 ///                 },
364 ///                 value: Some(ast::Pattern {
365 ///                     elements: vec![
366 ///                         ast::PatternElement::TextElement {
367 ///                             value: "Welcome, "
368 ///                         },
369 ///                         ast::PatternElement::Placeable {
370 ///                             expression: ast::Expression::Inline(
371 ///                                 ast::InlineExpression::VariableReference {
372 ///                                     id: ast::Identifier {
373 ///                                         name: "userName"
374 ///                                     }
375 ///                                 }
376 ///                             )
377 ///                         },
378 ///                         ast::PatternElement::TextElement {
379 ///                             value: "."
380 ///                         }
381 ///                     ]
382 ///                 }),
383 ///                 attributes: vec![],
384 ///                 comment: None,
385 ///             }),
386 ///         ]
387 ///     }
388 /// );
389 /// ```
390 #[derive(Debug, PartialEq, Clone)]
391 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
392 pub struct Pattern<S> {
393     pub elements: Vec<PatternElement<S>>,
394 }
395 
396 /// PatternElement is an element of a [`Pattern`].
397 ///
398 /// Each [`PatternElement`] node represents
399 /// either a simple textual value, or a combination of text literals
400 /// and placeholder [`Expression`] nodes.
401 ///
402 /// # Example
403 ///
404 /// ```
405 /// use fluent_syntax::parser;
406 /// use fluent_syntax::ast;
407 ///
408 /// let ftl = r#"
409 ///
410 /// hello-world = Hello, World!
411 ///
412 /// welcome = Welcome, { $userName }.
413 ///
414 /// "#;
415 ///
416 /// let resource = parser::parse(ftl)
417 ///     .expect("Failed to parse an FTL resource.");
418 ///
419 /// assert_eq!(
420 ///     resource,
421 ///     ast::Resource {
422 ///         body: vec![
423 ///             ast::Entry::Message(ast::Message {
424 ///                 id: ast::Identifier {
425 ///                     name: "hello-world"
426 ///                 },
427 ///                 value: Some(ast::Pattern {
428 ///                     elements: vec![
429 ///                         ast::PatternElement::TextElement {
430 ///                             value: "Hello, World!"
431 ///                         }
432 ///                     ]
433 ///                 }),
434 ///                 attributes: vec![],
435 ///                 comment: None,
436 ///             }),
437 ///             ast::Entry::Message(ast::Message {
438 ///                 id: ast::Identifier {
439 ///                     name: "welcome"
440 ///                 },
441 ///                 value: Some(ast::Pattern {
442 ///                     elements: vec![
443 ///                         ast::PatternElement::TextElement {
444 ///                             value: "Welcome, "
445 ///                         },
446 ///                         ast::PatternElement::Placeable {
447 ///                             expression: ast::Expression::Inline(
448 ///                                 ast::InlineExpression::VariableReference {
449 ///                                     id: ast::Identifier {
450 ///                                         name: "userName"
451 ///                                     }
452 ///                                 }
453 ///                             )
454 ///                         },
455 ///                         ast::PatternElement::TextElement {
456 ///                             value: "."
457 ///                         }
458 ///                     ]
459 ///                 }),
460 ///                 attributes: vec![],
461 ///                 comment: None,
462 ///             }),
463 ///         ]
464 ///     }
465 /// );
466 /// ```
467 #[derive(Debug, PartialEq, Clone)]
468 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
469 #[cfg_attr(feature = "serde", serde(tag = "type"))]
470 pub enum PatternElement<S> {
471     TextElement { value: S },
472     Placeable { expression: Expression<S> },
473 }
474 
475 /// Attribute represents a part of a [`Message`] or [`Term`].
476 ///
477 /// Attributes are used to express a compound list of keyed
478 /// [`Pattern`] elements on an entry.
479 ///
480 /// # Example
481 ///
482 /// ```
483 /// use fluent_syntax::parser;
484 /// use fluent_syntax::ast;
485 ///
486 /// let ftl = r#"
487 ///
488 /// hello-world =
489 ///     .title = This is a title
490 ///     .accesskey = T
491 ///
492 /// "#;
493 ///
494 /// let resource = parser::parse(ftl)
495 ///     .expect("Failed to parse an FTL resource.");
496 ///
497 /// assert_eq!(
498 ///     resource,
499 ///     ast::Resource {
500 ///         body: vec![
501 ///             ast::Entry::Message(ast::Message {
502 ///                 id: ast::Identifier {
503 ///                     name: "hello-world"
504 ///                 },
505 ///                 value: None,
506 ///                 attributes: vec![
507 ///                     ast::Attribute {
508 ///                         id: ast::Identifier {
509 ///                             name: "title"
510 ///                         },
511 ///                         value: ast::Pattern {
512 ///                             elements: vec![
513 ///                                 ast::PatternElement::TextElement {
514 ///                                     value: "This is a title"
515 ///                                 },
516 ///                             ]
517 ///                         }
518 ///                     },
519 ///                     ast::Attribute {
520 ///                         id: ast::Identifier {
521 ///                             name: "accesskey"
522 ///                         },
523 ///                         value: ast::Pattern {
524 ///                             elements: vec![
525 ///                                 ast::PatternElement::TextElement {
526 ///                                     value: "T"
527 ///                                 },
528 ///                             ]
529 ///                         }
530 ///                     }
531 ///                 ],
532 ///                 comment: None,
533 ///             }),
534 ///         ]
535 ///     }
536 /// );
537 /// ```
538 #[derive(Debug, PartialEq, Clone)]
539 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
540 pub struct Attribute<S> {
541     pub id: Identifier<S>,
542     pub value: Pattern<S>,
543 }
544 
545 /// Identifier is part of nodes such as [`Message`], [`Term`] and [`Attribute`].
546 ///
547 /// It is used to associate a unique key with an [`Entry`] or an [`Attribute`]
548 /// and in [`Expression`] nodes to refer to another entry.
549 ///
550 /// # Example
551 ///
552 /// ```
553 /// use fluent_syntax::parser;
554 /// use fluent_syntax::ast;
555 ///
556 /// let ftl = r#"
557 ///
558 /// hello-world = Value
559 ///
560 /// "#;
561 ///
562 /// let resource = parser::parse(ftl)
563 ///     .expect("Failed to parse an FTL resource.");
564 ///
565 /// assert_eq!(
566 ///     resource,
567 ///     ast::Resource {
568 ///         body: vec![
569 ///             ast::Entry::Message(ast::Message {
570 ///                 id: ast::Identifier {
571 ///                     name: "hello-world"
572 ///                 },
573 ///                 value: Some(ast::Pattern {
574 ///                     elements: vec![
575 ///                         ast::PatternElement::TextElement {
576 ///                             value: "Value"
577 ///                         }
578 ///                     ]
579 ///                 }),
580 ///                 attributes: vec![],
581 ///                 comment: None,
582 ///             }),
583 ///         ]
584 ///     }
585 /// );
586 /// ```
587 #[derive(Debug, PartialEq, Clone)]
588 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
589 pub struct Identifier<S> {
590     pub name: S,
591 }
592 
593 /// Variant is a single branch of a value in a [`Select`](Expression::Select) expression.
594 ///
595 /// It's a pair of [`VariantKey`] and [`Pattern`]. If the selector match the
596 /// key, then the value of the variant is returned as the value of the expression.
597 ///
598 /// # Example
599 ///
600 /// ```
601 /// use fluent_syntax::parser;
602 /// use fluent_syntax::ast;
603 ///
604 /// let ftl = r#"
605 ///
606 /// hello-world = { $var ->
607 ///     [key1] Value 1
608 ///    *[other] Value 2
609 /// }
610 ///
611 /// "#;
612 ///
613 /// let resource = parser::parse(ftl)
614 ///     .expect("Failed to parse an FTL resource.");
615 ///
616 /// assert_eq!(
617 ///     resource,
618 ///     ast::Resource {
619 ///         body: vec![
620 ///             ast::Entry::Message(ast::Message {
621 ///                 id: ast::Identifier {
622 ///                     name: "hello-world"
623 ///                 },
624 ///                 value: Some(ast::Pattern {
625 ///                     elements: vec![
626 ///                         ast::PatternElement::Placeable {
627 ///                             expression: ast::Expression::Select {
628 ///                                 selector: ast::InlineExpression::VariableReference {
629 ///                                     id: ast::Identifier { name: "var" },
630 ///                                 },
631 ///                                 variants: vec![
632 ///                                     ast::Variant {
633 ///                                         key: ast::VariantKey::Identifier {
634 ///                                             name: "key1"
635 ///                                         },
636 ///                                         value: ast::Pattern {
637 ///                                             elements: vec![
638 ///                                                 ast::PatternElement::TextElement {
639 ///                                                     value: "Value 1",
640 ///                                                 }
641 ///                                             ]
642 ///                                         },
643 ///                                         default: false,
644 ///                                     },
645 ///                                     ast::Variant {
646 ///                                         key: ast::VariantKey::Identifier {
647 ///                                             name: "other"
648 ///                                         },
649 ///                                         value: ast::Pattern {
650 ///                                             elements: vec![
651 ///                                                 ast::PatternElement::TextElement {
652 ///                                                     value: "Value 2",
653 ///                                                 }
654 ///                                             ]
655 ///                                         },
656 ///                                         default: true,
657 ///                                     },
658 ///                                 ]
659 ///                             }
660 ///                         }
661 ///                     ]
662 ///                 }),
663 ///                 attributes: vec![],
664 ///                 comment: None,
665 ///             }),
666 ///         ]
667 ///     }
668 /// );
669 /// ```
670 #[derive(Debug, PartialEq, Clone)]
671 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
672 #[cfg_attr(feature = "serde", serde(tag = "type"))]
673 pub struct Variant<S> {
674     pub key: VariantKey<S>,
675     pub value: Pattern<S>,
676     pub default: bool,
677 }
678 
679 /// A key of a [`Variant`].
680 ///
681 /// Variant key can be either an identifier or a number.
682 ///
683 /// # Example
684 ///
685 /// ```
686 /// use fluent_syntax::parser;
687 /// use fluent_syntax::ast;
688 ///
689 /// let ftl = r#"
690 ///
691 /// hello-world = { $var ->
692 ///     [0] Value 1
693 ///    *[other] Value 2
694 /// }
695 ///
696 /// "#;
697 ///
698 /// let resource = parser::parse(ftl)
699 ///     .expect("Failed to parse an FTL resource.");
700 ///
701 /// assert_eq!(
702 ///     resource,
703 ///     ast::Resource {
704 ///         body: vec![
705 ///             ast::Entry::Message(ast::Message {
706 ///                 id: ast::Identifier {
707 ///                     name: "hello-world"
708 ///                 },
709 ///                 value: Some(ast::Pattern {
710 ///                     elements: vec![
711 ///                         ast::PatternElement::Placeable {
712 ///                             expression: ast::Expression::Select {
713 ///                                 selector: ast::InlineExpression::VariableReference {
714 ///                                     id: ast::Identifier { name: "var" },
715 ///                                 },
716 ///                                 variants: vec![
717 ///                                     ast::Variant {
718 ///                                         key: ast::VariantKey::NumberLiteral {
719 ///                                             value: "0"
720 ///                                         },
721 ///                                         value: ast::Pattern {
722 ///                                             elements: vec![
723 ///                                                 ast::PatternElement::TextElement {
724 ///                                                     value: "Value 1",
725 ///                                                 }
726 ///                                             ]
727 ///                                         },
728 ///                                         default: false,
729 ///                                     },
730 ///                                     ast::Variant {
731 ///                                         key: ast::VariantKey::Identifier {
732 ///                                             name: "other"
733 ///                                         },
734 ///                                         value: ast::Pattern {
735 ///                                             elements: vec![
736 ///                                                 ast::PatternElement::TextElement {
737 ///                                                     value: "Value 2",
738 ///                                                 }
739 ///                                             ]
740 ///                                         },
741 ///                                         default: true,
742 ///                                     },
743 ///                                 ]
744 ///                             }
745 ///                         }
746 ///                     ]
747 ///                 }),
748 ///                 attributes: vec![],
749 ///                 comment: None,
750 ///             }),
751 ///         ]
752 ///     }
753 /// );
754 /// ```
755 #[derive(Debug, PartialEq, Clone)]
756 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
757 #[cfg_attr(feature = "serde", serde(tag = "type"))]
758 pub enum VariantKey<S> {
759     Identifier { name: S },
760     NumberLiteral { value: S },
761 }
762 
763 /// Fluent [`Comment`].
764 ///
765 /// In Fluent, comments may be standalone, or associated with
766 /// an entry such as [`Term`] or [`Message`].
767 ///
768 /// When used as a standalone [`Entry`], comments may appear in one of
769 /// three levels:
770 ///
771 /// * Standalone comment
772 /// * Group comment associated with a group of messages
773 /// * Resource comment associated with the whole resource
774 ///
775 /// # Example
776 ///
777 /// ```
778 /// use fluent_syntax::parser;
779 /// use fluent_syntax::ast;
780 ///
781 /// let ftl = r#"
782 /// ## A standalone level comment
783 /// "#;
784 ///
785 /// let resource = parser::parse(ftl)
786 ///     .expect("Failed to parse an FTL resource.");
787 ///
788 /// assert_eq!(
789 ///     resource,
790 ///     ast::Resource {
791 ///         body: vec![
792 ///             ast::Entry::Comment(ast::Comment {
793 ///                 content: vec![
794 ///                     "A standalone level comment"
795 ///                 ]
796 ///             })
797 ///         ]
798 ///     }
799 /// );
800 /// ```
801 #[derive(Debug, PartialEq, Clone)]
802 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
803 #[cfg_attr(feature = "serde", serde(from = "helper::CommentDef<S>"))]
804 pub struct Comment<S> {
805     pub content: Vec<S>,
806 }
807 
808 /// List of arguments for a [`FunctionReference`](InlineExpression::FunctionReference) or a
809 /// [`TermReference`](InlineExpression::TermReference).
810 ///
811 /// Function and Term reference may contain a list of positional and
812 /// named arguments passed to them.
813 ///
814 /// # Example
815 ///
816 /// ```
817 /// use fluent_syntax::parser;
818 /// use fluent_syntax::ast;
819 ///
820 /// let ftl = r#"
821 ///
822 /// key = { FUNC($var1, "literal", style: "long") }
823 ///
824 /// "#;
825 ///
826 /// let resource = parser::parse(ftl)
827 ///     .expect("Failed to parse an FTL resource.");
828 ///
829 /// assert_eq!(
830 ///     resource,
831 ///     ast::Resource {
832 ///         body: vec![
833 ///             ast::Entry::Message(
834 ///                 ast::Message {
835 ///                     id: ast::Identifier {
836 ///                         name: "key"
837 ///                     },
838 ///                     value: Some(ast::Pattern {
839 ///                         elements: vec![
840 ///                             ast::PatternElement::Placeable {
841 ///                                 expression: ast::Expression::Inline(
842 ///                                     ast::InlineExpression::FunctionReference {
843 ///                                         id: ast::Identifier {
844 ///                                             name: "FUNC"
845 ///                                         },
846 ///                                         arguments: ast::CallArguments {
847 ///                                             positional: vec![
848 ///                                                 ast::InlineExpression::VariableReference {
849 ///                                                     id: ast::Identifier {
850 ///                                                         name: "var1"
851 ///                                                     }
852 ///                                                 },
853 ///                                                 ast::InlineExpression::StringLiteral {
854 ///                                                     value: "literal",
855 ///                                                 }
856 ///                                             ],
857 ///                                             named: vec![
858 ///                                                 ast::NamedArgument {
859 ///                                                     name: ast::Identifier {
860 ///                                                         name: "style"
861 ///                                                     },
862 ///                                                     value: ast::InlineExpression::StringLiteral
863 ///                                                     {
864 ///                                                         value: "long"
865 ///                                                     }
866 ///                                                 }
867 ///                                             ],
868 ///                                         }
869 ///                                     }
870 ///                                 )
871 ///                             },
872 ///                         ]
873 ///                     }),
874 ///                     attributes: vec![],
875 ///                     comment: None,
876 ///                 }
877 ///             )
878 ///         ]
879 ///     }
880 /// );
881 /// ```
882 #[derive(Debug, PartialEq, Clone, Default)]
883 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
884 #[cfg_attr(feature = "serde", serde(tag = "type"))]
885 pub struct CallArguments<S> {
886     pub positional: Vec<InlineExpression<S>>,
887     pub named: Vec<NamedArgument<S>>,
888 }
889 
890 /// A key-value pair used in [`CallArguments`].
891 ///
892 /// # Example
893 ///
894 /// ```
895 /// use fluent_syntax::parser;
896 /// use fluent_syntax::ast;
897 ///
898 /// let ftl = r#"
899 ///
900 /// key = { FUNC(style: "long") }
901 ///
902 /// "#;
903 ///
904 /// let resource = parser::parse(ftl)
905 ///     .expect("Failed to parse an FTL resource.");
906 ///
907 /// assert_eq!(
908 ///     resource,
909 ///     ast::Resource {
910 ///         body: vec![
911 ///             ast::Entry::Message(
912 ///                 ast::Message {
913 ///                     id: ast::Identifier {
914 ///                         name: "key"
915 ///                     },
916 ///                     value: Some(ast::Pattern {
917 ///                         elements: vec![
918 ///                             ast::PatternElement::Placeable {
919 ///                                 expression: ast::Expression::Inline(
920 ///                                     ast::InlineExpression::FunctionReference {
921 ///                                         id: ast::Identifier {
922 ///                                             name: "FUNC"
923 ///                                         },
924 ///                                         arguments: ast::CallArguments {
925 ///                                             positional: vec![],
926 ///                                             named: vec![
927 ///                                                 ast::NamedArgument {
928 ///                                                     name: ast::Identifier {
929 ///                                                         name: "style"
930 ///                                                     },
931 ///                                                     value: ast::InlineExpression::StringLiteral
932 ///                                                     {
933 ///                                                         value: "long"
934 ///                                                     }
935 ///                                                 }
936 ///                                             ],
937 ///                                         }
938 ///                                     }
939 ///                                 )
940 ///                             },
941 ///                         ]
942 ///                     }),
943 ///                     attributes: vec![],
944 ///                     comment: None,
945 ///                 }
946 ///             )
947 ///         ]
948 ///     }
949 /// );
950 /// ```
951 #[derive(Debug, PartialEq, Clone)]
952 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
953 #[cfg_attr(feature = "serde", serde(tag = "type"))]
954 pub struct NamedArgument<S> {
955     pub name: Identifier<S>,
956     pub value: InlineExpression<S>,
957 }
958 
959 /// A subset of expressions which can be used as [`Placeable`](PatternElement::Placeable),
960 /// [`selector`](Expression::Select), or in [`CallArguments`].
961 ///
962 /// # Example
963 ///
964 /// ```
965 /// use fluent_syntax::parser;
966 /// use fluent_syntax::ast;
967 ///
968 /// let ftl = r#"
969 ///
970 /// key = { $emailCount }
971 ///
972 /// "#;
973 ///
974 /// let resource = parser::parse(ftl)
975 ///     .expect("Failed to parse an FTL resource.");
976 ///
977 /// assert_eq!(
978 ///     resource,
979 ///     ast::Resource {
980 ///         body: vec![
981 ///             ast::Entry::Message(
982 ///                 ast::Message {
983 ///                     id: ast::Identifier {
984 ///                         name: "key"
985 ///                     },
986 ///                     value: Some(ast::Pattern {
987 ///                         elements: vec![
988 ///                             ast::PatternElement::Placeable {
989 ///                                 expression: ast::Expression::Inline(
990 ///                                     ast::InlineExpression::VariableReference {
991 ///                                         id: ast::Identifier {
992 ///                                             name: "emailCount"
993 ///                                         },
994 ///                                     }
995 ///                                 )
996 ///                             },
997 ///                         ]
998 ///                     }),
999 ///                     attributes: vec![],
1000 ///                     comment: None,
1001 ///                 }
1002 ///             )
1003 ///         ]
1004 ///     }
1005 /// );
1006 /// ```
1007 #[derive(Debug, PartialEq, Clone)]
1008 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1009 #[cfg_attr(feature = "serde", serde(tag = "type"))]
1010 pub enum InlineExpression<S> {
1011     /// Single line string literal enclosed in `"`.
1012     ///
1013     /// # Example
1014     ///
1015     /// ```
1016     /// use fluent_syntax::parser;
1017     /// use fluent_syntax::ast;
1018     ///
1019     /// let ftl = r#"
1020     ///
1021     /// key = { "this is a literal" }
1022     ///
1023     /// "#;
1024     ///
1025     /// let resource = parser::parse(ftl)
1026     ///     .expect("Failed to parse an FTL resource.");
1027     ///
1028     /// assert_eq!(
1029     ///     resource,
1030     ///     ast::Resource {
1031     ///         body: vec![
1032     ///             ast::Entry::Message(
1033     ///                 ast::Message {
1034     ///                     id: ast::Identifier {
1035     ///                         name: "key"
1036     ///                     },
1037     ///                     value: Some(ast::Pattern {
1038     ///                         elements: vec![
1039     ///                             ast::PatternElement::Placeable {
1040     ///                                 expression: ast::Expression::Inline(
1041     ///                                     ast::InlineExpression::StringLiteral {
1042     ///                                         value: "this is a literal",
1043     ///                                     }
1044     ///                                 )
1045     ///                             },
1046     ///                         ]
1047     ///                     }),
1048     ///                     attributes: vec![],
1049     ///                     comment: None,
1050     ///                 }
1051     ///             )
1052     ///         ]
1053     ///     }
1054     /// );
1055     /// ```
1056     StringLiteral { value: S },
1057     /// A number literal.
1058     ///
1059     /// # Example
1060     ///
1061     /// ```
1062     /// use fluent_syntax::parser;
1063     /// use fluent_syntax::ast;
1064     ///
1065     /// let ftl = r#"
1066     ///
1067     /// key = { -0.5 }
1068     ///
1069     /// "#;
1070     ///
1071     /// let resource = parser::parse(ftl)
1072     ///     .expect("Failed to parse an FTL resource.");
1073     ///
1074     /// assert_eq!(
1075     ///     resource,
1076     ///     ast::Resource {
1077     ///         body: vec![
1078     ///             ast::Entry::Message(
1079     ///                 ast::Message {
1080     ///                     id: ast::Identifier {
1081     ///                         name: "key"
1082     ///                     },
1083     ///                     value: Some(ast::Pattern {
1084     ///                         elements: vec![
1085     ///                             ast::PatternElement::Placeable {
1086     ///                                 expression: ast::Expression::Inline(
1087     ///                                     ast::InlineExpression::NumberLiteral {
1088     ///                                         value: "-0.5",
1089     ///                                     }
1090     ///                                 )
1091     ///                             },
1092     ///                         ]
1093     ///                     }),
1094     ///                     attributes: vec![],
1095     ///                     comment: None,
1096     ///                 }
1097     ///             )
1098     ///         ]
1099     ///     }
1100     /// );
1101     /// ```
1102     NumberLiteral { value: S },
1103     /// A function reference.
1104     ///
1105     /// # Example
1106     ///
1107     /// ```
1108     /// use fluent_syntax::parser;
1109     /// use fluent_syntax::ast;
1110     ///
1111     /// let ftl = r#"
1112     ///
1113     /// key = { FUNC() }
1114     ///
1115     /// "#;
1116     ///
1117     /// let resource = parser::parse(ftl)
1118     ///     .expect("Failed to parse an FTL resource.");
1119     ///
1120     /// assert_eq!(
1121     ///     resource,
1122     ///     ast::Resource {
1123     ///         body: vec![
1124     ///             ast::Entry::Message(
1125     ///                 ast::Message {
1126     ///                     id: ast::Identifier {
1127     ///                         name: "key"
1128     ///                     },
1129     ///                     value: Some(ast::Pattern {
1130     ///                         elements: vec![
1131     ///                             ast::PatternElement::Placeable {
1132     ///                                 expression: ast::Expression::Inline(
1133     ///                                     ast::InlineExpression::FunctionReference {
1134     ///                                         id: ast::Identifier {
1135     ///                                             name: "FUNC"
1136     ///                                         },
1137     ///                                         arguments: ast::CallArguments::default(),
1138     ///                                     }
1139     ///                                 )
1140     ///                             },
1141     ///                         ]
1142     ///                     }),
1143     ///                     attributes: vec![],
1144     ///                     comment: None,
1145     ///                 }
1146     ///             )
1147     ///         ]
1148     ///     }
1149     /// );
1150     /// ```
1151     FunctionReference {
1152         id: Identifier<S>,
1153         arguments: CallArguments<S>,
1154     },
1155     /// A reference to another message.
1156     ///
1157     /// # Example
1158     ///
1159     /// ```
1160     /// use fluent_syntax::parser;
1161     /// use fluent_syntax::ast;
1162     ///
1163     /// let ftl = r#"
1164     ///
1165     /// key = { key2 }
1166     ///
1167     /// "#;
1168     ///
1169     /// let resource = parser::parse(ftl)
1170     ///     .expect("Failed to parse an FTL resource.");
1171     ///
1172     /// assert_eq!(
1173     ///     resource,
1174     ///     ast::Resource {
1175     ///         body: vec![
1176     ///             ast::Entry::Message(
1177     ///                 ast::Message {
1178     ///                     id: ast::Identifier {
1179     ///                         name: "key"
1180     ///                     },
1181     ///                     value: Some(ast::Pattern {
1182     ///                         elements: vec![
1183     ///                             ast::PatternElement::Placeable {
1184     ///                                 expression: ast::Expression::Inline(
1185     ///                                     ast::InlineExpression::MessageReference {
1186     ///                                         id: ast::Identifier {
1187     ///                                             name: "key2"
1188     ///                                         },
1189     ///                                         attribute: None,
1190     ///                                     }
1191     ///                                 )
1192     ///                             },
1193     ///                         ]
1194     ///                     }),
1195     ///                     attributes: vec![],
1196     ///                     comment: None,
1197     ///                 }
1198     ///             )
1199     ///         ]
1200     ///     }
1201     /// );
1202     /// ```
1203     MessageReference {
1204         id: Identifier<S>,
1205         attribute: Option<Identifier<S>>,
1206     },
1207     /// A reference to a term.
1208     ///
1209     /// # Example
1210     ///
1211     /// ```
1212     /// use fluent_syntax::parser;
1213     /// use fluent_syntax::ast;
1214     ///
1215     /// let ftl = r#"
1216     ///
1217     /// key = { -brand-name }
1218     ///
1219     /// "#;
1220     ///
1221     /// let resource = parser::parse(ftl)
1222     ///     .expect("Failed to parse an FTL resource.");
1223     ///
1224     /// assert_eq!(
1225     ///     resource,
1226     ///     ast::Resource {
1227     ///         body: vec![
1228     ///             ast::Entry::Message(
1229     ///                 ast::Message {
1230     ///                     id: ast::Identifier {
1231     ///                         name: "key"
1232     ///                     },
1233     ///                     value: Some(ast::Pattern {
1234     ///                         elements: vec![
1235     ///                             ast::PatternElement::Placeable {
1236     ///                                 expression: ast::Expression::Inline(
1237     ///                                     ast::InlineExpression::TermReference {
1238     ///                                         id: ast::Identifier {
1239     ///                                             name: "brand-name"
1240     ///                                         },
1241     ///                                         attribute: None,
1242     ///                                         arguments: None,
1243     ///                                     }
1244     ///                                 )
1245     ///                             },
1246     ///                         ]
1247     ///                     }),
1248     ///                     attributes: vec![],
1249     ///                     comment: None,
1250     ///                 }
1251     ///             )
1252     ///         ]
1253     ///     }
1254     /// );
1255     /// ```
1256     TermReference {
1257         id: Identifier<S>,
1258         attribute: Option<Identifier<S>>,
1259         arguments: Option<CallArguments<S>>,
1260     },
1261     /// A reference to a variable.
1262     ///
1263     /// # Example
1264     ///
1265     /// ```
1266     /// use fluent_syntax::parser;
1267     /// use fluent_syntax::ast;
1268     ///
1269     /// let ftl = r#"
1270     ///
1271     /// key = { $var1 }
1272     ///
1273     /// "#;
1274     ///
1275     /// let resource = parser::parse(ftl)
1276     ///     .expect("Failed to parse an FTL resource.");
1277     ///
1278     /// assert_eq!(
1279     ///     resource,
1280     ///     ast::Resource {
1281     ///         body: vec![
1282     ///             ast::Entry::Message(
1283     ///                 ast::Message {
1284     ///                     id: ast::Identifier {
1285     ///                         name: "key"
1286     ///                     },
1287     ///                     value: Some(ast::Pattern {
1288     ///                         elements: vec![
1289     ///                             ast::PatternElement::Placeable {
1290     ///                                 expression: ast::Expression::Inline(
1291     ///                                     ast::InlineExpression::VariableReference {
1292     ///                                         id: ast::Identifier {
1293     ///                                             name: "var1"
1294     ///                                         },
1295     ///                                     }
1296     ///                                 )
1297     ///                             },
1298     ///                         ]
1299     ///                     }),
1300     ///                     attributes: vec![],
1301     ///                     comment: None,
1302     ///                 }
1303     ///             )
1304     ///         ]
1305     ///     }
1306     /// );
1307     /// ```
1308     VariableReference { id: Identifier<S> },
1309     /// A placeable which may contain another expression.
1310     ///
1311     /// # Example
1312     ///
1313     /// ```
1314     /// use fluent_syntax::parser;
1315     /// use fluent_syntax::ast;
1316     ///
1317     /// let ftl = r#"
1318     ///
1319     /// key = { { "placeable" } }
1320     ///
1321     /// "#;
1322     ///
1323     /// let resource = parser::parse(ftl)
1324     ///     .expect("Failed to parse an FTL resource.");
1325     ///
1326     /// assert_eq!(
1327     ///     resource,
1328     ///     ast::Resource {
1329     ///         body: vec![
1330     ///             ast::Entry::Message(
1331     ///                 ast::Message {
1332     ///                     id: ast::Identifier {
1333     ///                         name: "key"
1334     ///                     },
1335     ///                     value: Some(ast::Pattern {
1336     ///                         elements: vec![
1337     ///                             ast::PatternElement::Placeable {
1338     ///                                 expression: ast::Expression::Inline(
1339     ///                                     ast::InlineExpression::Placeable {
1340     ///                                         expression: Box::new(
1341     ///                                             ast::Expression::Inline(
1342     ///                                                 ast::InlineExpression::StringLiteral {
1343     ///                                                     value: "placeable"
1344     ///                                                 }
1345     ///                                             )
1346     ///                                         )
1347     ///                                     }
1348     ///                                 )
1349     ///                             },
1350     ///                         ]
1351     ///                     }),
1352     ///                     attributes: vec![],
1353     ///                     comment: None,
1354     ///                 }
1355     ///             )
1356     ///         ]
1357     ///     }
1358     /// );
1359     /// ```
1360     Placeable { expression: Box<Expression<S>> },
1361 }
1362 
1363 /// An expression that is either a select expression or an inline expression.
1364 ///
1365 /// # Example
1366 ///
1367 /// ```
1368 /// use fluent_syntax::parser;
1369 /// use fluent_syntax::ast;
1370 ///
1371 /// let ftl = r#"
1372 ///
1373 /// key = { $var ->
1374 ///     [key1] Value 1
1375 ///    *[other] Value 2
1376 /// }
1377 ///
1378 /// "#;
1379 ///
1380 /// let resource = parser::parse(ftl)
1381 ///     .expect("Failed to parse an FTL resource.");
1382 ///
1383 /// assert_eq!(
1384 ///     resource,
1385 ///     ast::Resource {
1386 ///         body: vec![
1387 ///             ast::Entry::Message(ast::Message {
1388 ///                 id: ast::Identifier {
1389 ///                     name: "key"
1390 ///                 },
1391 ///                 value: Some(ast::Pattern {
1392 ///                     elements: vec![
1393 ///                         ast::PatternElement::Placeable {
1394 ///                             expression: ast::Expression::Select {
1395 ///                                 selector: ast::InlineExpression::VariableReference {
1396 ///                                     id: ast::Identifier { name: "var" },
1397 ///                                 },
1398 ///                                 variants: vec![
1399 ///                                     ast::Variant {
1400 ///                                         key: ast::VariantKey::Identifier {
1401 ///                                             name: "key1"
1402 ///                                         },
1403 ///                                         value: ast::Pattern {
1404 ///                                             elements: vec![
1405 ///                                                 ast::PatternElement::TextElement {
1406 ///                                                     value: "Value 1",
1407 ///                                                 }
1408 ///                                             ]
1409 ///                                         },
1410 ///                                         default: false,
1411 ///                                     },
1412 ///                                     ast::Variant {
1413 ///                                         key: ast::VariantKey::Identifier {
1414 ///                                             name: "other"
1415 ///                                         },
1416 ///                                         value: ast::Pattern {
1417 ///                                             elements: vec![
1418 ///                                                 ast::PatternElement::TextElement {
1419 ///                                                     value: "Value 2",
1420 ///                                                 }
1421 ///                                             ]
1422 ///                                         },
1423 ///                                         default: true,
1424 ///                                     },
1425 ///                                 ]
1426 ///                             }
1427 ///                         }
1428 ///                     ]
1429 ///                 }),
1430 ///                 attributes: vec![],
1431 ///                 comment: None,
1432 ///             }),
1433 ///         ]
1434 ///     }
1435 /// );
1436 /// ```
1437 #[derive(Debug, PartialEq, Clone)]
1438 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1439 #[cfg_attr(feature = "serde", serde(untagged))]
1440 pub enum Expression<S> {
1441     Select {
1442         selector: InlineExpression<S>,
1443         variants: Vec<Variant<S>>,
1444     },
1445     Inline(InlineExpression<S>),
1446 }
1447