//! Abstract Syntax Tree representation of the Fluent Translation List. //! //! The AST of Fluent contains all nodes structures to represent a complete //! representation of the FTL resource. //! //! The tree preserves all semantic information and allow for round-trip //! of a canonically written FTL resource. //! //! The root node is called [`Resource`] and contains a list of [`Entry`] nodes //! representing all possible entries in the Fluent Translation List. //! //! # Example //! //! ``` //! use fluent_syntax::parser; //! use fluent_syntax::ast; //! //! let ftl = r#" //! //! ## This is a message comment //! hello-world = Hello World! //! .tooltip = Tooltip for you, { $userName }. //! //! "#; //! //! let resource = parser::parse(ftl) //! .expect("Failed to parse an FTL resource."); //! //! assert_eq!( //! resource.body[0], //! ast::Entry::Message( //! ast::Message { //! id: ast::Identifier { //! name: "hello-world" //! }, //! value: Some(ast::Pattern { //! elements: vec![ //! ast::PatternElement::TextElement { //! value: "Hello World!" //! }, //! ] //! }), //! attributes: vec![ //! ast::Attribute { //! id: ast::Identifier { //! name: "tooltip" //! }, //! value: ast::Pattern { //! elements: vec![ //! ast::PatternElement::TextElement { //! value: "Tooltip for you, " //! }, //! ast::PatternElement::Placeable { //! expression: ast::Expression::Inline( //! ast::InlineExpression::VariableReference { //! id: ast::Identifier { //! name: "userName" //! } //! } //! ) //! }, //! ast::PatternElement::TextElement { //! value: "." //! }, //! ] //! } //! } //! ], //! comment: Some( //! ast::Comment { //! content: vec!["This is a message comment"] //! } //! ) //! } //! ), //! ); //! ``` //! //! ## Errors //! //! Fluent AST preserves blocks containing invaid syntax as [`Entry::Junk`]. //! //! ## White space //! //! At the moment, AST does not preserve white space. In result only a //! canonical form of the AST is suitable for a round-trip. mod helper; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; /// Root node of a Fluent Translation List. /// /// A [`Resource`] contains a body with a list of [`Entry`] nodes. /// /// # Example /// /// ``` /// use fluent_syntax::parser; /// use fluent_syntax::ast; /// /// let ftl = ""; /// /// let resource = parser::parse(ftl) /// .expect("Failed to parse an FTL resource."); /// /// assert_eq!( /// resource, /// ast::Resource { /// body: vec![] /// } /// ); /// ``` #[derive(Debug, PartialEq, Clone)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Resource { pub body: Vec>, } /// A top-level node representing an entry of a [`Resource`]. /// /// Every [`Entry`] is a standalone element and the parser is capable /// of recovering from errors by identifying a beginning of a next entry. /// /// # Example /// /// ``` /// use fluent_syntax::parser; /// use fluent_syntax::ast; /// /// let ftl = r#" /// /// key = Value /// /// "#; /// /// let resource = parser::parse(ftl) /// .expect("Failed to parse an FTL resource."); /// /// assert_eq!( /// resource, /// ast::Resource { /// body: vec![ /// ast::Entry::Message( /// ast::Message { /// id: ast::Identifier { /// name: "key" /// }, /// value: Some(ast::Pattern { /// elements: vec![ /// ast::PatternElement::TextElement { /// value: "Value" /// }, /// ] /// }), /// attributes: vec![], /// comment: None, /// } /// ) /// ] /// } /// ); /// ``` /// /// # Junk Entry /// /// If FTL source contains invalid FTL content, it will be preserved /// in form of [`Entry::Junk`] nodes. /// /// # Example /// /// ``` /// use fluent_syntax::parser; /// use fluent_syntax::ast; /// /// let ftl = r#" /// /// g@rb@ge En!ry /// /// "#; /// /// let (resource, _) = parser::parse(ftl) /// .expect_err("Failed to parse an FTL resource."); /// /// assert_eq!( /// resource, /// ast::Resource { /// body: vec![ /// ast::Entry::Junk { /// content: "g@rb@ge En!ry\n\n" /// } /// ] /// } /// ); /// ``` #[derive(Debug, PartialEq, Clone)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "type"))] pub enum Entry { Message(Message), Term(Term), Comment(Comment), GroupComment(Comment), ResourceComment(Comment), Junk { content: S }, } /// Message node represents the most common [`Entry`] in an FTL [`Resource`]. /// /// A message is a localization unit with a [`Identifier`] unique within a given /// [`Resource`], and a value or attributes with associated [`Pattern`]. /// /// A message can contain a simple text value, or a compound combination of value /// and attributes which together can be used to localize a complex User Interface /// element. /// /// Finally, each [`Message`] may have an associated [`Comment`]. /// /// # Example /// /// ``` /// use fluent_syntax::parser; /// use fluent_syntax::ast; /// /// let ftl = r#" /// /// hello-world = Hello, World! /// /// "#; /// /// let resource = parser::parse(ftl) /// .expect("Failed to parse an FTL resource."); /// /// assert_eq!( /// resource, /// ast::Resource { /// body: vec![ /// ast::Entry::Message(ast::Message { /// id: ast::Identifier { /// name: "hello-world" /// }, /// value: Some(ast::Pattern { /// elements: vec![ /// ast::PatternElement::TextElement { /// value: "Hello, World!" /// } /// ] /// }), /// attributes: vec![], /// comment: None, /// }) /// ] /// } /// ); /// ``` #[derive(Debug, PartialEq, Clone)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Message { pub id: Identifier, pub value: Option>, pub attributes: Vec>, pub comment: Option>, } /// A Fluent [`Term`]. /// /// Terms are semantically similar to [`Message`] nodes, but /// they represent a separate concept in Fluent system. /// /// Every term has to have a value, and the parser will /// report errors when term references are used in wrong positions. /// /// # Example /// /// ``` /// use fluent_syntax::parser; /// use fluent_syntax::ast; /// /// let ftl = r#" /// /// -brand-name = Nightly /// /// "#; /// /// let resource = parser::parse(ftl) /// .expect("Failed to parse an FTL resource."); /// /// assert_eq!( /// resource, /// ast::Resource { /// body: vec![ /// ast::Entry::Term(ast::Term { /// id: ast::Identifier { /// name: "brand-name" /// }, /// value: ast::Pattern { /// elements: vec![ /// ast::PatternElement::TextElement { /// value: "Nightly" /// } /// ] /// }, /// attributes: vec![], /// comment: None, /// }) /// ] /// } /// ); /// ``` #[derive(Debug, PartialEq, Clone)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Term { pub id: Identifier, pub value: Pattern, pub attributes: Vec>, pub comment: Option>, } /// Pattern contains a value of a [`Message`], [`Term`] or an [`Attribute`]. /// /// Each pattern is a list of [`PatternElement`] nodes representing /// either a simple textual value, or a combination of text literals /// and placeholder [`Expression`] nodes. /// /// # Example /// /// ``` /// use fluent_syntax::parser; /// use fluent_syntax::ast; /// /// let ftl = r#" /// /// hello-world = Hello, World! /// /// welcome = Welcome, { $userName }. /// /// "#; /// /// let resource = parser::parse(ftl) /// .expect("Failed to parse an FTL resource."); /// /// assert_eq!( /// resource, /// ast::Resource { /// body: vec![ /// ast::Entry::Message(ast::Message { /// id: ast::Identifier { /// name: "hello-world" /// }, /// value: Some(ast::Pattern { /// elements: vec![ /// ast::PatternElement::TextElement { /// value: "Hello, World!" /// } /// ] /// }), /// attributes: vec![], /// comment: None, /// }), /// ast::Entry::Message(ast::Message { /// id: ast::Identifier { /// name: "welcome" /// }, /// value: Some(ast::Pattern { /// elements: vec![ /// ast::PatternElement::TextElement { /// value: "Welcome, " /// }, /// ast::PatternElement::Placeable { /// expression: ast::Expression::Inline( /// ast::InlineExpression::VariableReference { /// id: ast::Identifier { /// name: "userName" /// } /// } /// ) /// }, /// ast::PatternElement::TextElement { /// value: "." /// } /// ] /// }), /// attributes: vec![], /// comment: None, /// }), /// ] /// } /// ); /// ``` #[derive(Debug, PartialEq, Clone)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Pattern { pub elements: Vec>, } /// PatternElement is an element of a [`Pattern`]. /// /// Each [`PatternElement`] node represents /// either a simple textual value, or a combination of text literals /// and placeholder [`Expression`] nodes. /// /// # Example /// /// ``` /// use fluent_syntax::parser; /// use fluent_syntax::ast; /// /// let ftl = r#" /// /// hello-world = Hello, World! /// /// welcome = Welcome, { $userName }. /// /// "#; /// /// let resource = parser::parse(ftl) /// .expect("Failed to parse an FTL resource."); /// /// assert_eq!( /// resource, /// ast::Resource { /// body: vec![ /// ast::Entry::Message(ast::Message { /// id: ast::Identifier { /// name: "hello-world" /// }, /// value: Some(ast::Pattern { /// elements: vec![ /// ast::PatternElement::TextElement { /// value: "Hello, World!" /// } /// ] /// }), /// attributes: vec![], /// comment: None, /// }), /// ast::Entry::Message(ast::Message { /// id: ast::Identifier { /// name: "welcome" /// }, /// value: Some(ast::Pattern { /// elements: vec![ /// ast::PatternElement::TextElement { /// value: "Welcome, " /// }, /// ast::PatternElement::Placeable { /// expression: ast::Expression::Inline( /// ast::InlineExpression::VariableReference { /// id: ast::Identifier { /// name: "userName" /// } /// } /// ) /// }, /// ast::PatternElement::TextElement { /// value: "." /// } /// ] /// }), /// attributes: vec![], /// comment: None, /// }), /// ] /// } /// ); /// ``` #[derive(Debug, PartialEq, Clone)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "type"))] pub enum PatternElement { TextElement { value: S }, Placeable { expression: Expression }, } /// Attribute represents a part of a [`Message`] or [`Term`]. /// /// Attributes are used to express a compound list of keyed /// [`Pattern`] elements on an entry. /// /// # Example /// /// ``` /// use fluent_syntax::parser; /// use fluent_syntax::ast; /// /// let ftl = r#" /// /// hello-world = /// .title = This is a title /// .accesskey = T /// /// "#; /// /// let resource = parser::parse(ftl) /// .expect("Failed to parse an FTL resource."); /// /// assert_eq!( /// resource, /// ast::Resource { /// body: vec![ /// ast::Entry::Message(ast::Message { /// id: ast::Identifier { /// name: "hello-world" /// }, /// value: None, /// attributes: vec![ /// ast::Attribute { /// id: ast::Identifier { /// name: "title" /// }, /// value: ast::Pattern { /// elements: vec![ /// ast::PatternElement::TextElement { /// value: "This is a title" /// }, /// ] /// } /// }, /// ast::Attribute { /// id: ast::Identifier { /// name: "accesskey" /// }, /// value: ast::Pattern { /// elements: vec![ /// ast::PatternElement::TextElement { /// value: "T" /// }, /// ] /// } /// } /// ], /// comment: None, /// }), /// ] /// } /// ); /// ``` #[derive(Debug, PartialEq, Clone)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Attribute { pub id: Identifier, pub value: Pattern, } /// Identifier is part of nodes such as [`Message`], [`Term`] and [`Attribute`]. /// /// It is used to associate a unique key with an [`Entry`] or an [`Attribute`] /// and in [`Expression`] nodes to refer to another entry. /// /// # Example /// /// ``` /// use fluent_syntax::parser; /// use fluent_syntax::ast; /// /// let ftl = r#" /// /// hello-world = Value /// /// "#; /// /// let resource = parser::parse(ftl) /// .expect("Failed to parse an FTL resource."); /// /// assert_eq!( /// resource, /// ast::Resource { /// body: vec![ /// ast::Entry::Message(ast::Message { /// id: ast::Identifier { /// name: "hello-world" /// }, /// value: Some(ast::Pattern { /// elements: vec![ /// ast::PatternElement::TextElement { /// value: "Value" /// } /// ] /// }), /// attributes: vec![], /// comment: None, /// }), /// ] /// } /// ); /// ``` #[derive(Debug, PartialEq, Clone)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Identifier { pub name: S, } /// Variant is a single branch of a value in a [`Select`](Expression::Select) expression. /// /// It's a pair of [`VariantKey`] and [`Pattern`]. If the selector match the /// key, then the value of the variant is returned as the value of the expression. /// /// # Example /// /// ``` /// use fluent_syntax::parser; /// use fluent_syntax::ast; /// /// let ftl = r#" /// /// hello-world = { $var -> /// [key1] Value 1 /// *[other] Value 2 /// } /// /// "#; /// /// let resource = parser::parse(ftl) /// .expect("Failed to parse an FTL resource."); /// /// assert_eq!( /// resource, /// ast::Resource { /// body: vec![ /// ast::Entry::Message(ast::Message { /// id: ast::Identifier { /// name: "hello-world" /// }, /// value: Some(ast::Pattern { /// elements: vec![ /// ast::PatternElement::Placeable { /// expression: ast::Expression::Select { /// selector: ast::InlineExpression::VariableReference { /// id: ast::Identifier { name: "var" }, /// }, /// variants: vec![ /// ast::Variant { /// key: ast::VariantKey::Identifier { /// name: "key1" /// }, /// value: ast::Pattern { /// elements: vec![ /// ast::PatternElement::TextElement { /// value: "Value 1", /// } /// ] /// }, /// default: false, /// }, /// ast::Variant { /// key: ast::VariantKey::Identifier { /// name: "other" /// }, /// value: ast::Pattern { /// elements: vec![ /// ast::PatternElement::TextElement { /// value: "Value 2", /// } /// ] /// }, /// default: true, /// }, /// ] /// } /// } /// ] /// }), /// attributes: vec![], /// comment: None, /// }), /// ] /// } /// ); /// ``` #[derive(Debug, PartialEq, Clone)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "type"))] pub struct Variant { pub key: VariantKey, pub value: Pattern, pub default: bool, } /// A key of a [`Variant`]. /// /// Variant key can be either an identifier or a number. /// /// # Example /// /// ``` /// use fluent_syntax::parser; /// use fluent_syntax::ast; /// /// let ftl = r#" /// /// hello-world = { $var -> /// [0] Value 1 /// *[other] Value 2 /// } /// /// "#; /// /// let resource = parser::parse(ftl) /// .expect("Failed to parse an FTL resource."); /// /// assert_eq!( /// resource, /// ast::Resource { /// body: vec![ /// ast::Entry::Message(ast::Message { /// id: ast::Identifier { /// name: "hello-world" /// }, /// value: Some(ast::Pattern { /// elements: vec![ /// ast::PatternElement::Placeable { /// expression: ast::Expression::Select { /// selector: ast::InlineExpression::VariableReference { /// id: ast::Identifier { name: "var" }, /// }, /// variants: vec![ /// ast::Variant { /// key: ast::VariantKey::NumberLiteral { /// value: "0" /// }, /// value: ast::Pattern { /// elements: vec![ /// ast::PatternElement::TextElement { /// value: "Value 1", /// } /// ] /// }, /// default: false, /// }, /// ast::Variant { /// key: ast::VariantKey::Identifier { /// name: "other" /// }, /// value: ast::Pattern { /// elements: vec![ /// ast::PatternElement::TextElement { /// value: "Value 2", /// } /// ] /// }, /// default: true, /// }, /// ] /// } /// } /// ] /// }), /// attributes: vec![], /// comment: None, /// }), /// ] /// } /// ); /// ``` #[derive(Debug, PartialEq, Clone)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "type"))] pub enum VariantKey { Identifier { name: S }, NumberLiteral { value: S }, } /// Fluent [`Comment`]. /// /// In Fluent, comments may be standalone, or associated with /// an entry such as [`Term`] or [`Message`]. /// /// When used as a standalone [`Entry`], comments may appear in one of /// three levels: /// /// * Standalone comment /// * Group comment associated with a group of messages /// * Resource comment associated with the whole resource /// /// # Example /// /// ``` /// use fluent_syntax::parser; /// use fluent_syntax::ast; /// /// let ftl = r#" /// ## A standalone level comment /// "#; /// /// let resource = parser::parse(ftl) /// .expect("Failed to parse an FTL resource."); /// /// assert_eq!( /// resource, /// ast::Resource { /// body: vec![ /// ast::Entry::Comment(ast::Comment { /// content: vec![ /// "A standalone level comment" /// ] /// }) /// ] /// } /// ); /// ``` #[derive(Debug, PartialEq, Clone)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(from = "helper::CommentDef"))] pub struct Comment { pub content: Vec, } /// List of arguments for a [`FunctionReference`](InlineExpression::FunctionReference) or a /// [`TermReference`](InlineExpression::TermReference). /// /// Function and Term reference may contain a list of positional and /// named arguments passed to them. /// /// # Example /// /// ``` /// use fluent_syntax::parser; /// use fluent_syntax::ast; /// /// let ftl = r#" /// /// key = { FUNC($var1, "literal", style: "long") } /// /// "#; /// /// let resource = parser::parse(ftl) /// .expect("Failed to parse an FTL resource."); /// /// assert_eq!( /// resource, /// ast::Resource { /// body: vec![ /// ast::Entry::Message( /// ast::Message { /// id: ast::Identifier { /// name: "key" /// }, /// value: Some(ast::Pattern { /// elements: vec![ /// ast::PatternElement::Placeable { /// expression: ast::Expression::Inline( /// ast::InlineExpression::FunctionReference { /// id: ast::Identifier { /// name: "FUNC" /// }, /// arguments: ast::CallArguments { /// positional: vec![ /// ast::InlineExpression::VariableReference { /// id: ast::Identifier { /// name: "var1" /// } /// }, /// ast::InlineExpression::StringLiteral { /// value: "literal", /// } /// ], /// named: vec![ /// ast::NamedArgument { /// name: ast::Identifier { /// name: "style" /// }, /// value: ast::InlineExpression::StringLiteral /// { /// value: "long" /// } /// } /// ], /// } /// } /// ) /// }, /// ] /// }), /// attributes: vec![], /// comment: None, /// } /// ) /// ] /// } /// ); /// ``` #[derive(Debug, PartialEq, Clone, Default)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "type"))] pub struct CallArguments { pub positional: Vec>, pub named: Vec>, } /// A key-value pair used in [`CallArguments`]. /// /// # Example /// /// ``` /// use fluent_syntax::parser; /// use fluent_syntax::ast; /// /// let ftl = r#" /// /// key = { FUNC(style: "long") } /// /// "#; /// /// let resource = parser::parse(ftl) /// .expect("Failed to parse an FTL resource."); /// /// assert_eq!( /// resource, /// ast::Resource { /// body: vec![ /// ast::Entry::Message( /// ast::Message { /// id: ast::Identifier { /// name: "key" /// }, /// value: Some(ast::Pattern { /// elements: vec![ /// ast::PatternElement::Placeable { /// expression: ast::Expression::Inline( /// ast::InlineExpression::FunctionReference { /// id: ast::Identifier { /// name: "FUNC" /// }, /// arguments: ast::CallArguments { /// positional: vec![], /// named: vec![ /// ast::NamedArgument { /// name: ast::Identifier { /// name: "style" /// }, /// value: ast::InlineExpression::StringLiteral /// { /// value: "long" /// } /// } /// ], /// } /// } /// ) /// }, /// ] /// }), /// attributes: vec![], /// comment: None, /// } /// ) /// ] /// } /// ); /// ``` #[derive(Debug, PartialEq, Clone)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "type"))] pub struct NamedArgument { pub name: Identifier, pub value: InlineExpression, } /// A subset of expressions which can be used as [`Placeable`](PatternElement::Placeable), /// [`selector`](Expression::Select), or in [`CallArguments`]. /// /// # Example /// /// ``` /// use fluent_syntax::parser; /// use fluent_syntax::ast; /// /// let ftl = r#" /// /// key = { $emailCount } /// /// "#; /// /// let resource = parser::parse(ftl) /// .expect("Failed to parse an FTL resource."); /// /// assert_eq!( /// resource, /// ast::Resource { /// body: vec![ /// ast::Entry::Message( /// ast::Message { /// id: ast::Identifier { /// name: "key" /// }, /// value: Some(ast::Pattern { /// elements: vec![ /// ast::PatternElement::Placeable { /// expression: ast::Expression::Inline( /// ast::InlineExpression::VariableReference { /// id: ast::Identifier { /// name: "emailCount" /// }, /// } /// ) /// }, /// ] /// }), /// attributes: vec![], /// comment: None, /// } /// ) /// ] /// } /// ); /// ``` #[derive(Debug, PartialEq, Clone)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "type"))] pub enum InlineExpression { /// Single line string literal enclosed in `"`. /// /// # Example /// /// ``` /// use fluent_syntax::parser; /// use fluent_syntax::ast; /// /// let ftl = r#" /// /// key = { "this is a literal" } /// /// "#; /// /// let resource = parser::parse(ftl) /// .expect("Failed to parse an FTL resource."); /// /// assert_eq!( /// resource, /// ast::Resource { /// body: vec![ /// ast::Entry::Message( /// ast::Message { /// id: ast::Identifier { /// name: "key" /// }, /// value: Some(ast::Pattern { /// elements: vec![ /// ast::PatternElement::Placeable { /// expression: ast::Expression::Inline( /// ast::InlineExpression::StringLiteral { /// value: "this is a literal", /// } /// ) /// }, /// ] /// }), /// attributes: vec![], /// comment: None, /// } /// ) /// ] /// } /// ); /// ``` StringLiteral { value: S }, /// A number literal. /// /// # Example /// /// ``` /// use fluent_syntax::parser; /// use fluent_syntax::ast; /// /// let ftl = r#" /// /// key = { -0.5 } /// /// "#; /// /// let resource = parser::parse(ftl) /// .expect("Failed to parse an FTL resource."); /// /// assert_eq!( /// resource, /// ast::Resource { /// body: vec![ /// ast::Entry::Message( /// ast::Message { /// id: ast::Identifier { /// name: "key" /// }, /// value: Some(ast::Pattern { /// elements: vec![ /// ast::PatternElement::Placeable { /// expression: ast::Expression::Inline( /// ast::InlineExpression::NumberLiteral { /// value: "-0.5", /// } /// ) /// }, /// ] /// }), /// attributes: vec![], /// comment: None, /// } /// ) /// ] /// } /// ); /// ``` NumberLiteral { value: S }, /// A function reference. /// /// # Example /// /// ``` /// use fluent_syntax::parser; /// use fluent_syntax::ast; /// /// let ftl = r#" /// /// key = { FUNC() } /// /// "#; /// /// let resource = parser::parse(ftl) /// .expect("Failed to parse an FTL resource."); /// /// assert_eq!( /// resource, /// ast::Resource { /// body: vec![ /// ast::Entry::Message( /// ast::Message { /// id: ast::Identifier { /// name: "key" /// }, /// value: Some(ast::Pattern { /// elements: vec![ /// ast::PatternElement::Placeable { /// expression: ast::Expression::Inline( /// ast::InlineExpression::FunctionReference { /// id: ast::Identifier { /// name: "FUNC" /// }, /// arguments: ast::CallArguments::default(), /// } /// ) /// }, /// ] /// }), /// attributes: vec![], /// comment: None, /// } /// ) /// ] /// } /// ); /// ``` FunctionReference { id: Identifier, arguments: CallArguments, }, /// A reference to another message. /// /// # Example /// /// ``` /// use fluent_syntax::parser; /// use fluent_syntax::ast; /// /// let ftl = r#" /// /// key = { key2 } /// /// "#; /// /// let resource = parser::parse(ftl) /// .expect("Failed to parse an FTL resource."); /// /// assert_eq!( /// resource, /// ast::Resource { /// body: vec![ /// ast::Entry::Message( /// ast::Message { /// id: ast::Identifier { /// name: "key" /// }, /// value: Some(ast::Pattern { /// elements: vec![ /// ast::PatternElement::Placeable { /// expression: ast::Expression::Inline( /// ast::InlineExpression::MessageReference { /// id: ast::Identifier { /// name: "key2" /// }, /// attribute: None, /// } /// ) /// }, /// ] /// }), /// attributes: vec![], /// comment: None, /// } /// ) /// ] /// } /// ); /// ``` MessageReference { id: Identifier, attribute: Option>, }, /// A reference to a term. /// /// # Example /// /// ``` /// use fluent_syntax::parser; /// use fluent_syntax::ast; /// /// let ftl = r#" /// /// key = { -brand-name } /// /// "#; /// /// let resource = parser::parse(ftl) /// .expect("Failed to parse an FTL resource."); /// /// assert_eq!( /// resource, /// ast::Resource { /// body: vec![ /// ast::Entry::Message( /// ast::Message { /// id: ast::Identifier { /// name: "key" /// }, /// value: Some(ast::Pattern { /// elements: vec![ /// ast::PatternElement::Placeable { /// expression: ast::Expression::Inline( /// ast::InlineExpression::TermReference { /// id: ast::Identifier { /// name: "brand-name" /// }, /// attribute: None, /// arguments: None, /// } /// ) /// }, /// ] /// }), /// attributes: vec![], /// comment: None, /// } /// ) /// ] /// } /// ); /// ``` TermReference { id: Identifier, attribute: Option>, arguments: Option>, }, /// A reference to a variable. /// /// # Example /// /// ``` /// use fluent_syntax::parser; /// use fluent_syntax::ast; /// /// let ftl = r#" /// /// key = { $var1 } /// /// "#; /// /// let resource = parser::parse(ftl) /// .expect("Failed to parse an FTL resource."); /// /// assert_eq!( /// resource, /// ast::Resource { /// body: vec![ /// ast::Entry::Message( /// ast::Message { /// id: ast::Identifier { /// name: "key" /// }, /// value: Some(ast::Pattern { /// elements: vec![ /// ast::PatternElement::Placeable { /// expression: ast::Expression::Inline( /// ast::InlineExpression::VariableReference { /// id: ast::Identifier { /// name: "var1" /// }, /// } /// ) /// }, /// ] /// }), /// attributes: vec![], /// comment: None, /// } /// ) /// ] /// } /// ); /// ``` VariableReference { id: Identifier }, /// A placeable which may contain another expression. /// /// # Example /// /// ``` /// use fluent_syntax::parser; /// use fluent_syntax::ast; /// /// let ftl = r#" /// /// key = { { "placeable" } } /// /// "#; /// /// let resource = parser::parse(ftl) /// .expect("Failed to parse an FTL resource."); /// /// assert_eq!( /// resource, /// ast::Resource { /// body: vec![ /// ast::Entry::Message( /// ast::Message { /// id: ast::Identifier { /// name: "key" /// }, /// value: Some(ast::Pattern { /// elements: vec![ /// ast::PatternElement::Placeable { /// expression: ast::Expression::Inline( /// ast::InlineExpression::Placeable { /// expression: Box::new( /// ast::Expression::Inline( /// ast::InlineExpression::StringLiteral { /// value: "placeable" /// } /// ) /// ) /// } /// ) /// }, /// ] /// }), /// attributes: vec![], /// comment: None, /// } /// ) /// ] /// } /// ); /// ``` Placeable { expression: Box> }, } /// An expression that is either a select expression or an inline expression. /// /// # Example /// /// ``` /// use fluent_syntax::parser; /// use fluent_syntax::ast; /// /// let ftl = r#" /// /// key = { $var -> /// [key1] Value 1 /// *[other] Value 2 /// } /// /// "#; /// /// let resource = parser::parse(ftl) /// .expect("Failed to parse an FTL resource."); /// /// assert_eq!( /// resource, /// ast::Resource { /// body: vec![ /// ast::Entry::Message(ast::Message { /// id: ast::Identifier { /// name: "key" /// }, /// value: Some(ast::Pattern { /// elements: vec![ /// ast::PatternElement::Placeable { /// expression: ast::Expression::Select { /// selector: ast::InlineExpression::VariableReference { /// id: ast::Identifier { name: "var" }, /// }, /// variants: vec![ /// ast::Variant { /// key: ast::VariantKey::Identifier { /// name: "key1" /// }, /// value: ast::Pattern { /// elements: vec![ /// ast::PatternElement::TextElement { /// value: "Value 1", /// } /// ] /// }, /// default: false, /// }, /// ast::Variant { /// key: ast::VariantKey::Identifier { /// name: "other" /// }, /// value: ast::Pattern { /// elements: vec![ /// ast::PatternElement::TextElement { /// value: "Value 2", /// } /// ] /// }, /// default: true, /// }, /// ] /// } /// } /// ] /// }), /// attributes: vec![], /// comment: None, /// }), /// ] /// } /// ); /// ``` #[derive(Debug, PartialEq, Clone)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(untagged))] pub enum Expression { Select { selector: InlineExpression, variants: Vec>, }, Inline(InlineExpression), }