diff --git a/spec/data-model/README.md b/spec/data-model/README.md index 6632211e90..7ba584d548 100644 --- a/spec/data-model/README.md +++ b/spec/data-model/README.md @@ -43,8 +43,8 @@ interface SelectMessage { ``` Each message _declaration_ is represented by a `Declaration`, -which connects the `name` of the left-hand side _variable_ -with its right-hand side `value`. +which connects the `name` of the _variable_ +with its _expression_ `value`. The `name` does not include the initial `$` of the _variable_. ```ts diff --git a/spec/formatting.md b/spec/formatting.md index 1ee03aa9d7..61d05b5bb2 100644 --- a/spec/formatting.md +++ b/spec/formatting.md @@ -7,7 +7,7 @@ when formatting a message for display in a user interface, or for some later pro To start, we presume that a _message_ has either been parsed from its syntax or created from a data model description. -If this construction has encountered any Syntax or Data Model errors, +If this construction has encountered any Syntax or Data Model Errors, their handling during formatting is specified here as well. Formatting of a _message_ is defined by the following operations: @@ -67,7 +67,7 @@ At a minimum, it includes: This will be used by strategies for bidirectional isolation, and can be used to set the base direction of the _message_ upon display. -- A mapping of string identifiers to values, +- An **_input mapping_** of string identifiers to values, defining variable values that are available during _variable resolution_. This is often determined by a user-provided argument of a formatting function call. @@ -83,11 +83,15 @@ Implementations MAY include additional fields in their _formatting context_. _Expressions_ are used in _declarations_, _selectors_, and _patterns_. -In a _declaration_, the resolved value of the _expression_ is assigned to a _variable_, +In a _declaration_, the resolved value of the _expression_ is bound to a _variable_, which is available for use by later _expressions_. Since a _variable_ can be referenced in different ways later, implementations SHOULD NOT immediately fully format the value for output. +In an _input-declaration_, the _variable_ operand of the _variable-expression_ +identifies not only the name of the external input value, +but also the _variable_ to which the resolved value of the _variable-expression_ is bound. + In _selectors_, the resolved value of an _expression_ is used for _pattern selection_. In a _pattern_, the resolved value of an _expression_ is used in its _formatting_. @@ -106,7 +110,7 @@ and different implementations MAY choose to perform different levels of resoluti > Alternatively, it could be an instance of an ICU4J `FormattedNumber`, > or some other locally appropriate value. -Depending on the presence or absence of an _operand_ +Depending on the presence or absence of a _variable_ or _literal_ operand and a _function_, _private-use_, or _reserved_ _annotation_, the resolved value of the _expression_ is determined as follows: @@ -119,8 +123,7 @@ its resolved value is defined according to the implementation's specification. Else, if the _expression_ contains an _annotation_, its resolved value is defined by _function resolution_. -Else, the _expression_ will contain only an _operand_, -which consists of either a _literal_ or a _variable_. +Else, the _expression_ will contain only either a _literal_ or a _variable_. If the _expression_ consists of a _variable_, its resolved value is defined by _variable resolution_. @@ -150,9 +153,9 @@ its resolved value is defined by _literal resolution_. > an _annotation_ needs to be provided: > > ``` -> let $aNumber = {1234 :number} -> let $aDate = {|2023-08-30| :datetime} -> let $aFoo = {|some foo| :foo} +> local $aNumber = {1234 :number} +> local $aDate = {|2023-08-30| :datetime} +> local $aFoo = {|some foo| :foo} > {You have {42 :number}} > ``` @@ -175,13 +178,10 @@ The resolution of a _text_ or _literal_ token MUST always succeed. ### Variable Resolution To resolve the value of a _variable_, -its _name_ is used to identify either a local variable, -or a variable defined elsewhere. -If a local variable and an externally defined one use the same name, -the local variable takes precedence. - -It is an error for a local variable definition to -refer to a local variable that's defined after it in the message. +its _name_ is used to identify either a local variable or an input variable. +If a _declaration_ exists for the _variable_, its resolved value is used. +Otherwise, the _variable_ is an implicit reference to an input value, +and its value is looked up from the _formatting context_ _input mapping_. The resolution of a _variable_ MAY fail if no value is identified for its _name_. If this happens, an Unresolved Variable error MUST be emitted. @@ -272,12 +272,12 @@ rather than the _expression_ in the _selector_ or _pattern_. > attempting to format either of the following messages: > > ``` -> let $var = {|horse| :func} +> local $var = {|horse| :func} > {The value is {$var}.} > ``` > > ``` -> let $var = {|horse|} +> local $var = {|horse|} > {The value is {$var :func}.} > ``` > @@ -690,7 +690,7 @@ These are divided into the following categories: > ``` > > ``` - > let $var = {|no message body|} + > local $var = {|no message body|} > ``` - **Data Model errors** occur when a message is invalid due to @@ -744,16 +744,15 @@ These are divided into the following categories: > ``` > > ``` - > let $one = {|The one|} + > local $one = {|The one|} > match {$one} > when 1 {Value is one} > when * {Value is not one} > ``` > > ``` - > let $one = {|The one| :func} - > let $two = {$one} - > match {$two} + > input {$one} + > match {$one} > when 1 {Value is one} > when * {Value is not one} > ``` @@ -769,7 +768,7 @@ These are divided into the following categories: > ``` > > ``` - > let $foo = {horse :func one=1 two=2 one=1} + > local $foo = {horse :func one=1 two=2 one=1} > {This is {$foo}} > ``` @@ -844,7 +843,7 @@ These are divided into the following categories: > ``` > > ``` - > let $sel = {|horse| :plural} + > local $sel = {|horse| :plural} > match {$sel} > when 1 {The value is one.} > when * {The value is not one.} @@ -873,7 +872,7 @@ These are divided into the following categories: > ``` > > ``` - > let $id = {$user :get field=id} + > local $id = {$user :get field=id} > {Hello, {$id :get field=name}!} > ``` > diff --git a/spec/message.abnf b/spec/message.abnf index 30c3415584..33b82271cd 100644 --- a/spec/message.abnf +++ b/spec/message.abnf @@ -1,6 +1,9 @@ message = [s] *(declaration [s]) body [s] -declaration = let s variable [s] "=" [s] expression +declaration = input-declaration / local-declaration +input-declaration = input [s] variable-expression +local-declaration = local s variable [s] "=" [s] expression + body = pattern / (selectors 1*([s] variant)) @@ -9,8 +12,10 @@ selectors = match 1*([s] expression) variant = when 1*(s key) [s] pattern key = literal / "*" -expression = "{" [s] ((operand [s annotation]) / annotation) [s] "}" -operand = literal / variable +expression = literal-expression / variable-expression / function-expression +literal-expression = "{" [s] literal [s annotation] [s] "}" +variable-expression = "{" [s] variable [s annotation] [s] "}" +function-expression = "{" [s] annotation [s] "}" annotation = (function *(s option)) / reserved / private-use literal = quoted / unquoted @@ -19,7 +24,8 @@ function = (":" / "+" / "-") name option = name [s] "=" [s] (literal / variable) ; reserved keywords are always lowercase -let = %x6C.65.74 ; "let" +input = %x69.6E.70.75.74 ; "input" +local = %x6C.6F.63.61.6C ; "local" match = %x6D.61.74.63.68 ; "match" when = %x77.68.65.6E ; "when" diff --git a/spec/registry.md b/spec/registry.md index 6962fa7d29..222ecb5b3e 100644 --- a/spec/registry.md +++ b/spec/registry.md @@ -184,5 +184,5 @@ which expects the `plural` and `case` options: The following message references the second signature of `:adjective`, which only expects the `accord` option: - let $obj = {$object :noun case=nominative} - {You see {$color :adjective article=indefinite accord=$obj} {$obj}!} + input {$object :noun case=nominative} + {You see {$color :adjective article=indefinite accord=$object} {$object}!} diff --git a/spec/syntax.md b/spec/syntax.md index 099d5a4696..7091486f6b 100644 --- a/spec/syntax.md +++ b/spec/syntax.md @@ -85,9 +85,12 @@ The complete formal syntax of a _message_ is described by the [ABNF](./message.a ### Well-formed vs. Valid Messages A _message_ is **_well-formed_** if it satisfies all the rules of the grammar. +Attempting to parse a _message_ that is not _well-formed_ will result in a _Syntax Error_. -A _message_ is **_valid_** if it is _well-formed_ and **also** meets the additional content restrictions +A _message_ is **_valid_** if it is _well-formed_ and +**also** meets the additional content restrictions and semantic requirements about its structure defined below. +Attempting to parse a _message_ that is not _valid_ will result in a _Data Model Error_. ## The Message @@ -108,14 +111,14 @@ A **_message_** is the complete template for a specific message forma > > **Example** This _message_: > > > > ``` -> > let $foo = { |horse| } +> > local $foo = { |horse| } > > {You have a {$foo}!} > > ``` > > > > Can also be written as: > > > > ``` -> > let $foo={|horse|}{You have a {$foo}!} +> > local $foo={|horse|}{You have a {$foo}!} > > ``` > > > > An exception to this is: whitespace inside a _pattern_ is **always** significant. @@ -127,12 +130,27 @@ A _message_ consists of two parts: ### Declarations -A **_declaration_** binds a _variable_ identifier to the value of an _expression_ within the scope of a _message_. -This local variable can then be used in other _expressions_ within the same _message_. +A **_declaration_** binds a _variable_ identifier to a value within the scope of a _message_. +This _variable_ can then be used in other _expressions_ within the same _message_. _Declarations_ are optional: many messages will not contain any _declarations_. +An **_input-declaration_** binds a _variable_ to an external input value. +The _variable-expression_ of an _input-declaration_ +MAY include an _annotation_ that is applied to the external value. + +A **_local-declaration_** binds a _variable_ to the resolved value of an _expression_. + +Declared _variables_ MUST NOT be used before their _declaration_, +and their values MUST NOT be self-referential; +otherwise, a _message_ is not considered _valid_. + +Multiple _declarations_ MUST NOT bind a value to the same _variable_; +otherwise, a _message_ is not considered _valid_. + ```abnf -declaration = let s variable [s] "=" [s] expression +declaration = input-declaration / local-declaration +input-declaration = input [s] variable-expression +local-declaration = local s variable [s] "=" [s] expression ``` ### Body @@ -335,30 +353,39 @@ during the _message_'s formatting. An _expression_ MUST begin with U+007B LEFT CURLY BRACKET `{` and end with U+007D RIGHT CURLY BRACKET `}`. An _expression_ MUST NOT be empty. -An _expression_ can contain an _operand_, -an _annotation_, -or an _operand_ followed by an _annotation_. + +A **_literal-expression_** contains a _literal_, +optionally followed by an _annotation_. + +A **_variable-expression_** contains a _variable_, +optionally followed by an _annotation_. + +A **_function-expression_** contains only an _annotation_. ```abnf -expression = "{" [s] ((operand [s annotation]) / annotation) [s] "}" -operand = literal / variable +expression = literal-expression / variable-expression / function-expression +literal-expression = "{" [s] literal [s annotation] [s] "}" +variable-expression = "{" [s] variable [s annotation] [s] "}" +function-expression = "{" [s] annotation [s] "}" annotation = (function *(s option)) / private-use / reserved ``` There are several types of _expression_ that can appear in a _message_. All _expressions_ share a common syntax. The types of _expression_ are: -1. The value of a _declaration_ +1. The value of a _local-declaration_ 2. A _selector_ 3. A _placeholder_ in a _pattern_ +Additionally, an _input-declaration_ can contain a _variable-expression_. + > Examples of different types of _expression_ > > Declarations: > > ``` -> let $x = {|This is an expression|} -> let $y = {$operand :function option=operand} +> input {$x :function option=value} +> local $y = {|This is an expression|} > ``` > > Selectors: @@ -375,15 +402,6 @@ All _expressions_ share a common syntax. The types of _expression_ are: > {This placeholder references a function on a variable: {$variable :function with=options}} > ``` -### Operand - -An **_operand_** is a _literal_ or a _variable_ to be evaluated in an _expression_. -An _operand_ MAY optionally be followed by an _annotation_. - -```abnf -operand = literal / variable -``` - ### Annotation An **_annotation_** is part of an _expression_ containing either @@ -394,6 +412,9 @@ a _private-use_ or _reserved_ sequence. annotation = (function *(s option)) / reserved / private-use ``` +An **_operand_** is the _literal_ of a _literal-expression_ or +the _variable_ of a _variable-expression_. + An _annotation_ can appear in an _expression_ by itself or following a single _operand_. When following an _operand_, the _operand_ serves as input to the _annotation_. @@ -570,11 +591,12 @@ This section defines common elements used to construct _messages_. A **_keyword_** is a reserved token that has a unique meaning in the _message_ syntax. -The following three keywords are reserved: `let`, `match`, and `when`. +The following four keywords are reserved: `input`, `local`, `match`, and `when`. Reserved keywords are always lowercase. ```abnf -let = %x6C.65.74 ; "let" +input = %x69.6E.70.75.74 ; "input" +local = %x6C.6F.63.61.6C ; "local" match = %x6D.61.74.63.68 ; "match" when = %x77.68.65.6E ; "when" ``` @@ -583,9 +605,9 @@ when = %x77.68.65.6E ; "when" A **_literal_** is a character sequence that appears outside of _text_ in various parts of a _message_. -A _literal_ can appear in a _declaration_, +A _literal_ can appear as a _key_ value, -as an _operand_, +as the _operand_ of a _literal-expression_, or in the value of an _option_. A _literal_ MAY include any Unicode code point except for surrogate code points U+D800 through U+DFFF.