Skip to content

Use input and local keywords instead of let in declarations #488

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Oct 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions spec/data-model/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
51 changes: 25 additions & 26 deletions spec/formatting.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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 **_<dfn>input mapping</dfn>_** 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.

Expand All @@ -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_.
Expand All @@ -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:

Expand All @@ -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_.
Expand Down Expand Up @@ -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}}
> ```

Expand All @@ -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.
Expand Down Expand Up @@ -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}.}
> ```
>
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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}
> ```
Expand All @@ -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}}
> ```

Expand Down Expand Up @@ -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.}
Expand Down Expand Up @@ -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}!}
> ```
>
Expand Down
14 changes: 10 additions & 4 deletions spec/message.abnf
Original file line number Diff line number Diff line change
@@ -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))

Expand All @@ -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
Expand All @@ -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"

Expand Down
4 changes: 2 additions & 2 deletions spec/registry.md
Original file line number Diff line number Diff line change
Expand Up @@ -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}!}
76 changes: 49 additions & 27 deletions spec/syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 **_<dfn>well-formed</dfn>_** 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 **_<dfn>valid</dfn>_** if it is _well-formed_ and **also** meets the additional content restrictions
A _message_ is **_<dfn>valid</dfn>_** 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

Expand All @@ -108,14 +111,14 @@ A **_<dfn>message</dfn>_** 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.
Expand All @@ -127,12 +130,27 @@ A _message_ consists of two parts:

### Declarations

A **_<dfn>declaration</dfn>_** 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 **_<dfn>declaration</dfn>_** 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 **_<dfn>input-declaration</dfn>_** 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 **_<dfn>local-declaration</dfn>_** 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
Expand Down Expand Up @@ -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 **_<dfn>literal-expression</dfn>_** contains a _literal_,
optionally followed by an _annotation_.

A **_<dfn>variable-expression</dfn>_** contains a _variable_,
optionally followed by an _annotation_.

A **_<dfn>function-expression</dfn>_** 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:
Expand All @@ -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 **_<dfn>operand</dfn>_** 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 **_<dfn>annotation</dfn>_** is part of an _expression_ containing either
Expand All @@ -394,6 +412,9 @@ a _private-use_ or _reserved_ sequence.
annotation = (function *(s option)) / reserved / private-use
```

An **_<dfn>operand</dfn>_** 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_.

Expand Down Expand Up @@ -570,11 +591,12 @@ This section defines common elements used to construct _messages_.

A **_<dfn>keyword</dfn>_** 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"
```
Expand All @@ -583,9 +605,9 @@ when = %x77.68.65.6E ; "when"

A **_<dfn>literal</dfn>_** 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.
Expand Down