diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/rust/fluent-syntax/src/ast | |
parent | Initial commit. (diff) | |
download | firefox-esr-upstream.tar.xz firefox-esr-upstream.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | third_party/rust/fluent-syntax/src/ast/helper.rs | 25 | ||||
-rw-r--r-- | third_party/rust/fluent-syntax/src/ast/mod.rs | 1446 |
2 files changed, 1471 insertions, 0 deletions
diff --git a/third_party/rust/fluent-syntax/src/ast/helper.rs b/third_party/rust/fluent-syntax/src/ast/helper.rs new file mode 100644 index 0000000000..923437d23b --- /dev/null +++ b/third_party/rust/fluent-syntax/src/ast/helper.rs @@ -0,0 +1,25 @@ +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +use super::Comment; +// This is a helper struct used to properly deserialize referential +// JSON comments which are single continous String, into a vec of +// content slices. +#[derive(Debug, PartialEq, Clone)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde", serde(untagged))] +pub enum CommentDef<S> { + Single { content: S }, + Multi { content: Vec<S> }, +} + +impl<'s, S> From<CommentDef<S>> for Comment<S> { + fn from(input: CommentDef<S>) -> Self { + match input { + CommentDef::Single { content } => Self { + content: vec![content], + }, + CommentDef::Multi { content } => Self { content }, + } + } +} diff --git a/third_party/rust/fluent-syntax/src/ast/mod.rs b/third_party/rust/fluent-syntax/src/ast/mod.rs new file mode 100644 index 0000000000..5b79bb3e02 --- /dev/null +++ b/third_party/rust/fluent-syntax/src/ast/mod.rs @@ -0,0 +1,1446 @@ +//! 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<S> { + pub body: Vec<Entry<S>>, +} + +/// 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<S> { + Message(Message<S>), + Term(Term<S>), + Comment(Comment<S>), + GroupComment(Comment<S>), + ResourceComment(Comment<S>), + 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<S> { + pub id: Identifier<S>, + pub value: Option<Pattern<S>>, + pub attributes: Vec<Attribute<S>>, + pub comment: Option<Comment<S>>, +} + +/// 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<S> { + pub id: Identifier<S>, + pub value: Pattern<S>, + pub attributes: Vec<Attribute<S>>, + pub comment: Option<Comment<S>>, +} + +/// 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<S> { + pub elements: Vec<PatternElement<S>>, +} + +/// 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<S> { + TextElement { value: S }, + Placeable { expression: Expression<S> }, +} + +/// 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<S> { + pub id: Identifier<S>, + pub value: Pattern<S>, +} + +/// 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<S> { + 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<S> { + pub key: VariantKey<S>, + pub value: Pattern<S>, + 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<S> { + 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<S>"))] +pub struct Comment<S> { + pub content: Vec<S>, +} + +/// 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<S> { + pub positional: Vec<InlineExpression<S>>, + pub named: Vec<NamedArgument<S>>, +} + +/// 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<S> { + pub name: Identifier<S>, + pub value: InlineExpression<S>, +} + +/// 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<S> { + /// 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<S>, + arguments: CallArguments<S>, + }, + /// 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<S>, + attribute: Option<Identifier<S>>, + }, + /// 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<S>, + attribute: Option<Identifier<S>>, + arguments: Option<CallArguments<S>>, + }, + /// 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<S> }, + /// 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<Expression<S>> }, +} + +/// 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<S> { + Select { + selector: InlineExpression<S>, + variants: Vec<Variant<S>>, + }, + Inline(InlineExpression<S>), +} |