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.