fluent-syntax-1.0.0: src/Language/Fluent/AST.hs
{-# LANGUAGE DuplicateRecordFields #-}
-- | The abstract syntax tree of FTL, the <https://projectfluent.org Project Fluent> file format.
--
-- The documentation of each type links to the relevant chapter of the
-- <https://projectfluent.org/fluent/guide/ Fluent Syntax Guide>.
--
-- The normative reference is the <https://github.com/projectfluent/fluent/blob/master/spec/fluent.ebnf grammar>.
module Language.Fluent.AST where
import Data.Hashable (Hashable)
import Data.List.NonEmpty (NonEmpty)
import Data.Text (Text)
import GHC.Generics (Generic)
import Prelude
-- | An entire FTL file
newtype Resource = Resource {entries :: [Entry]}
deriving stock (Generic, Eq, Show)
-- | A top-level element of a t'Resource'
data Entry
= -- | A t'Message': @hello = Hello, world!@
MessageEntry Message
| -- | A t'Term': @-brand-name = Fluent@
TermEntry Term
| -- | A standalone @#@ comment, not attached to a t'Message' or t'Term'.
CommentEntry Comment
| -- | A @##@ comment, describing the group of entries that follows it.
GroupCommentEntry Comment
| -- | A @###@ comment, describing the whole t'Resource'.
ResourceCommentEntry Comment
| -- | Source text which could not be parsed, preserved verbatim.
-- See <https://projectfluent.org/fluent/guide/ the guide> on error recovery.
JunkEntry Text
deriving stock (Generic, Eq, Show)
-- | A translation unit. Either the @value@ or the @attributes@ must be present.
data Message = Message
{ id :: Identifier
-- ^ The name by which the message is referenced, e.g. @hello@.
, value :: Maybe Pattern
-- ^ The translation itself.
, attributes :: [Attribute]
-- ^ Additional translations belonging to this message.
, comment :: Maybe Comment
-- ^ The @#@ comment directly above the message, if any.
}
deriving stock (Generic, Eq, Show)
-- | A translation unit which is only ever referenced by other translations.
-- See <https://projectfluent.org/fluent/guide/terms.html Terms>.
data Term = Term
{ id :: Identifier
-- ^ The name by which the term is referenced, without the leading @-@.
-- e.g. @brand-name@ in @-brand-name@.
, value :: Pattern
-- ^ The translation itself; unlike a t'Message', a term always has one.
, attributes :: [Attribute]
-- ^ Additional translations belonging to this term, often grammatical
-- properties such as gender or case.
, comment :: Maybe Comment
-- ^ The @#@ comment directly above the term, if any.
}
deriving stock (Generic, Eq, Show)
-- | The text of a comment, with the @#@ prefixes and the space following them removed.
-- See <https://projectfluent.org/fluent/guide/comments.html Comments>.
newtype Comment = Comment Text
deriving stock (Generic, Eq, Show)
-- | A named t'Pattern' belonging to a t'Message' or a t'Term', written as
-- @.key = value@ on a line of its own.
-- See <https://projectfluent.org/fluent/guide/attributes.html Attributes>.
data Attribute = Attribute Identifier Pattern
deriving stock (Generic, Eq, Show)
-- | The value of a t'Message', t'Term', t'Attribute' or t'Variant': text interspersed
-- with placeables.
-- See <https://projectfluent.org/fluent/guide/text.html Writing Text>.
newtype Pattern = Pattern (NonEmpty PatternElement)
deriving stock (Generic, Eq, Show)
-- | A single piece of a t'Pattern'.
data PatternElement
= -- | Text on the first line of the pattern, or following a t'Placeable'.
InlineText Text
| -- | Text on a continuation line, already dedented according to the
-- <https://projectfluent.org/fluent/guide/multiline.html multiline> rules.
BlockText Text
| Placeable Placeable
deriving stock (Generic, Eq, Show)
-- | An 'Expression' embedded in a t'Pattern' between braces, e.g. @{ $userName }@.
-- See <https://projectfluent.org/fluent/guide/placeables.html Placeables>.
data Placeable
= -- | A placeable on the same line as the text preceding it.
InlinePlaceable Expression
| -- | A placeable on a continuation line, together with its indentation in
-- columns, which takes part in computing the common indent of the t'Pattern'.
BlockPlaceable Int Expression
deriving stock (Generic, Eq, Show)
-- | The 'Expression' of a t'Placeable', whether inline or block.
placeableExpression :: Placeable -> Expression
placeableExpression (InlinePlaceable e) = e
placeableExpression (BlockPlaceable _ e) = e
-- | A choice between several t'Variant's, made by matching the selector, an
-- 'InlineExpression', against the t'VariantKey's: @{ $count -> ... }@.
-- See <https://projectfluent.org/fluent/guide/selectors.html Selectors>.
data SelectExpression = SelectExpression InlineExpression VariantList
deriving stock (Generic, Eq, Show)
-- | An expression which yields a value.
data InlineExpression
= StringLiteralExpression StringLiteral
| NumberLiteralExpression NumberLiteral
| -- | A call of a built-in or implementation-provided function, e.g.
-- @NUMBER($ratio, minimumFractionDigits: 2)@.
-- See <https://projectfluent.org/fluent/guide/builtins.html Built-in Functions>
-- and <https://projectfluent.org/fluent/guide/functions.html Functions>.
FunctionReference Identifier CallArguments
| -- | A reference to another t'Message' or one of its attributes.
-- See <https://projectfluent.org/fluent/guide/references.html Referencing Messages>.
MessageReference Identifier (Maybe AttributeAccessor)
| -- | A reference to a t'Term' or one of its attributes, with optional arguments to
-- parameterise it.
-- See <https://projectfluent.org/fluent/guide/terms.html Terms>.
TermReference Identifier (Maybe AttributeAccessor) (Maybe CallArguments)
| -- | A reference to an argument passed in by the application, e.g. @$userName@.
-- See <https://projectfluent.org/fluent/guide/variables.html Variables>.
VariableReference Identifier
| -- | A placeable nested inside another placeable, used for grouping.
PlaceableExpression Expression
deriving stock (Generic, Eq, Show)
-- | The @.key@ suffix of a reference, selecting an t'Attribute' of the referent.
-- See <https://projectfluent.org/fluent/guide/attributes.html Attributes>.
newtype AttributeAccessor = AttributeAccessor Identifier
deriving stock (Generic, Eq, Show)
-- | The contents of a t'Placeable'.
-- See <https://projectfluent.org/fluent/guide/placeables.html Placeables>.
data Expression
= Select SelectExpression
| Inline InlineExpression
deriving stock (Generic, Eq, Show)
-- | One branch of a t'SelectExpression', written as @[key] value@ on a line of its own.
-- See <https://projectfluent.org/fluent/guide/selectors.html Selectors>.
data Variant = Variant
{ key :: VariantKey
-- ^ The value or plural category.
, value :: Pattern
-- ^ The translation for this variant.
, isDefault :: Bool
-- ^ Whether the variant is marked as default with @*@.
-- A default variant is used when no key matches.
}
deriving stock (Generic, Eq, Show)
-- | The key of a t'Variant': either a number, or a plural category such as @one@ or @other@.
-- See <https://projectfluent.org/fluent/guide/selectors.html Selectors>.
newtype VariantKey = VariantKey (Either NumberLiteral Identifier)
deriving stock (Generic, Eq, Show)
-- | The t'Variant's of a t'SelectExpression'. Exactly one of them is the default.
-- See <https://projectfluent.org/fluent/guide/selectors.html Selectors>.
newtype VariantList = VariantList (NonEmpty Variant)
deriving stock (Generic, Eq, Show)
-- | The parenthesised arguments of a 'FunctionReference' or 'TermReference'.
-- Positional arguments must precede named ones.
-- See <https://projectfluent.org/fluent/guide/functions.html Functions>.
newtype CallArguments = CallArguments [Either NamedArgument InlineExpression]
deriving stock (Generic, Eq, Show)
-- | An argument passed by name, e.g. @minimumFractionDigits: 2@.
-- See <https://projectfluent.org/fluent/guide/functions.html Functions>.
data NamedArgument = NamedArgument Identifier (Either StringLiteral NumberLiteral)
deriving stock (Generic, Eq, Show)
-- | The name of a t'Message', t'Term', t'Attribute', variable, function, or named argument.
newtype Identifier = Identifier Text
deriving newtype (Show, Eq, Ord, Hashable)
-- | A decimal number with arbitrary precision.
newtype NumberLiteral = NumberLiteral Text
deriving stock (Generic, Eq, Show)
-- | A quoted string, used to write characters which are otherwise
-- <https://projectfluent.org/fluent/guide/special.html special> in FTL.
data StringLiteral = StringLiteral
{ raw :: Text
-- ^ The source text between the quotes, with escape sequences unresolved.
, value :: Text
-- ^ The text the literal denotes, with escape sequences resolved.
}
deriving stock (Generic, Eq, Show)