Skip to content

Commit

Permalink
Fix fallback value definition and use (#903)
Browse files Browse the repository at this point in the history
* Fix references to fallback values

* Apply suggestions from code review

Co-authored-by: Addison Phillips <[email protected]>

* Apply suggestions from code review

Co-authored-by: Tim Chevalier <[email protected]>

* Apply suggestions from code review

Co-authored-by: Richard Gibson <[email protected]>

* Typo fix

* Note that operand options are not handled by Option Resolution

* Apply suggestions from code review

* Fix typo

Co-authored-by: Tim Chevalier <[email protected]>

* Use last rather than earliest operand for fallback value

* Use last name also during fallback in variable resolution

* Add tests for fallback formatting

* Tighten up language about fallback values, their string representations, and options

* Add "if supported" qualifier to emitting Bad Option error during option resolution

Co-authored-by: Addison Phillips <[email protected]>

---------

Co-authored-by: Addison Phillips <[email protected]>
Co-authored-by: Tim Chevalier <[email protected]>
Co-authored-by: Richard Gibson <[email protected]>
  • Loading branch information
4 people authored Nov 16, 2024
1 parent 852c5e2 commit f3344b5
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 75 deletions.
124 changes: 73 additions & 51 deletions spec/formatting.md
Original file line number Diff line number Diff line change
Expand Up @@ -231,8 +231,7 @@ whether its value was originally a _quoted literal_ or an _unquoted literal_.
> For example,
> the _option_ `foo=42` and the _option_ `foo=|42|` are treated as identical.
> For example, in a JavaScript formatter
> For example, in a JavaScript formatter,
> the _resolved value_ of a _text_ or a _literal_ could have the following implementation:
>
> ```ts
Expand All @@ -257,23 +256,30 @@ 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_ fails if no value is identified for its _name_.
If this happens, an _Unresolved Variable_ error is emitted.
If a _variable_ would resolve to a _fallback value_,
this MUST also be considered a failure.
If this happens, an _Unresolved Variable_ error is emitted
and a _fallback value_ is used as the _resolved value_ of the _variable_.
If the _resolved value_ identified for the _variable_ _name_ is a _fallback value_,
a _fallback value_ is used as the _resolved value_ of the _variable_.
The _fallback value_ representation of a _variable_ has a string representation
consisting of the U+0024 DOLLAR SIGN `$` followed by the _name_ of the _variable_.
### Function Resolution
To resolve an _expression_ with a _function_,
the following steps are taken:
1. If the _expression_ includes an _operand_, resolve its value.
If this fails, use a _fallback value_ for the _expression_.
2. Resolve the _identifier_ of the _function_ and, based on the starting sigil,
If this is a _fallback value_,
return a _fallback value_ as the _resolved value_ of the _expression_.
2. Resolve the _identifier_ of the _function_ and
find the appropriate _function handler_ to call.
If the implementation cannot find the _function handler_,
or if the _identifier_ includes a _namespace_ that the implementation does not support,
emit an _Unknown Function_ error
and use a _fallback value_ for the _expression_.
and return a _fallback value_ as the _resolved value_ of the _expression_.
Implementations are not required to implement _namespaces_ or installable
_function registries_.
Expand All @@ -294,7 +300,7 @@ the following steps are taken:
supported by the implementation, process them as specified.
Such `u:` options MAY be removed from the resolved mapping of _options_.
5. Call the function implementation with the following arguments:
5. Call the _function handler_ with the following arguments:
- The _function context_.
- The resolved mapping of _options_.
Expand All @@ -318,7 +324,7 @@ the following steps are taken:
_operand_ did not match that expected by the _function_,
the _function_ SHOULD cause a _Bad Operand_ error to be emitted.
In all failure cases, use the _fallback value_ for the _expression_ as its _resolved value_.
In all failure cases, return a _fallback value_ as the _resolved value_ of the _expression_.
#### Function Handler
Expand Down Expand Up @@ -366,17 +372,27 @@ The order of _options_ MUST NOT be significant.
For each _option_:
- Resolve the _identifier_ of the _option_.
- If the _option_'s right-hand side successfully resolves to a value,
bind the _identifier_ of the _option_ to the _resolved value_ in the mapping.
- Otherwise, bind the _identifier_ of the _option_ to an unresolved value in the mapping.
Implementations MAY later remove this value before calling the _function_.
(Note that an _Unresolved Variable_ error will have been emitted.)
1. Let `res` be a new empty mapping.
1. For each _option_:
1. Let `id` be the string value of the _identifier_ of the _option_.
1. Let `rv` be the _resolved value_ of the _option_ value.
1. If `rv` is a _fallback value_:
1. If supported, emit a _Bad Option_ error.
1. Else:
1. Set `res[id]` to be `rv`.
1. Return `res`.
Errors MAY be emitted during _option resolution_,
but it always resolves to some mapping of string identifiers to values.
The result of _option resolution_ MUST be a (possibly empty) mapping
of string identifiers to values;
that is, errors MAY be emitted, but such errors MUST NOT be fatal.
This mapping can be empty.
> [!NOTE]
> The _resolved value_ of a _function_ _operand_
> can also include resolved option values.
> These are not included in the _option resolution_ result,
> and need to be processed separately by a _function handler_.
### Markup Resolution
Unlike _functions_, the resolution of _markup_ is not customizable.
Expand All @@ -395,67 +411,73 @@ The resolution of _markup_ MUST always succeed.
### Fallback Resolution
A **_<dfn>fallback value</dfn>_** is the _resolved value_ for an _expression_ that fails to resolve.
A **_<dfn>fallback value</dfn>_** is the _resolved value_ for
an _expression_ or _variable_ when that _expression_ or _variable_ fails to resolve.
It contains a string representation that is used for its formatting,
and no option values.
The _resolved value_ of _text_, _literal_, and _markup_ MUST NOT be a _fallback value_.
A _variable_ fails to resolve when no value is identified for its _name_.
The string representation of its _fallback value_ is
U+0024 DOLLAR SIGN `$` followed by the _name_ of the _variable_.
An _expression_ fails to resolve when:
- A _variable_ used as an _operand_ (with or without a _function_) fails to resolve.
* Note that this does not include a _variable_ used as an _option_ value.
- A _function_ fails to resolve.
- A _variable_ used as its _operand_ resolves to a _fallback value_.
Note that an _expression_ does not necessarily fail to resolve
if an _option_ resolves with a _fallback value_.
- No _function handler_ is found for a _function_ _identifier_.
- Calling a _function handler_ fails or does not return a valid value.
The _fallback value_ depends on the contents of the _expression_:
The string representation of the _fallback value_ of an _expression_ depends on its contents:
- _expression_ with a _literal_ _operand_ (either quoted or unquoted)
- _expression_ with a _literal_ _operand_ (either quoted or unquoted):
U+007C VERTICAL LINE `|`
followed by the value of the _literal_
with escaping applied to U+005C REVERSE SOLIDUS `\` and U+007C VERTICAL LINE `|`,
and then by U+007C VERTICAL LINE `|`.
> Examples:
> In a context where `:func` fails to resolve,
> `{42 :func}` resolves to the _fallback value_ `|42|` and
> `{|C:\\| :func}` resolves to the _fallback value_ `|C:\\|`.
> `{42 :func}` resolves to a _fallback value_ with a string representation `|42|` and
> `{|C:\\| :func}` resolves to a _fallback value_ with a string representation `|C:\\|`.
- _expression_ with _variable_ _operand_ referring to a local _declaration_ (with or without a _function_):
the _value_ to which it resolves (which may already be a _fallback value_)
> Examples:
> In a context where `:func` fails to resolve,
> the _pattern_'s _expression_ in `.local $var={|val|} {{{$var :func}}}`
> resolves to the _fallback value_ `|val|` and the message formats to `{|val|}`.
> In a context where `:now` fails to resolve but `:datetime` does not,
> the _pattern_'s _expression_ in
> ```
> .local $t = {:now format=iso8601}
> .local $pretty_t = {$t :datetime}
> {{{$pretty_t}}}
> ```
> (transitively) resolves to the _fallback value_ `:now` and
> the message formats to `{:now}`.
- _expression_ with _variable_ _operand_ not referring to a local _declaration_ (with or without a _function_):
- _expression_ with _variable_ _operand_:
the _fallback value_ representation of that _variable_,
U+0024 DOLLAR SIGN `$` followed by the _name_ of the _variable_
> Examples:
> In a context where `$var` fails to resolve, `{$var}` and `{$var :number}`
> both resolve to the _fallback value_ `$var`.
> both resolve to a _fallback value_ with a string representation `$var`
> (even if `:number` fails to resolve).
>
> In a context where `:func` fails to resolve,
> the _pattern_'s _expression_ in `.input $arg {{{$arg :func}}}`
> resolves to the _fallback value_ `$arg` and
> the message formats to `{$arg}`.
> the _placeholder_ in `.local $var = {|val| :func} {{{$var}}}`
> resolves to a _fallback value_ with a string representation `$var`.
>
> In a context where either `:now` or `:pretty` fails to resolve,
> the _placeholder_ in
> ```
> .local $time = {:now format=iso8601}
> {{{$time :pretty}}}
> ```
> resolves to a _fallback value_ with a string representation `$time`.
- _function_ _expression_ with no _operand_:
U+003A COLON `:` followed by the _function_ _identifier_
> Examples:
> In a context where `:func` fails to resolve, `{:func}` resolves to the _fallback value_ `:func`.
> In a context where `:ns:func` fails to resolve, `{:ns:func}` resolves to the _fallback value_ `:ns:func`.
> In a context where `:func` fails to resolve,
> `{:func}` resolves to a _fallback value_ with a string representation `:func`.
> In a context where `:ns:func` fails to resolve,
> `{:ns:func}` resolves to a _fallback value_ with a string representation `:ns:func`.
- Otherwise: the U+FFFD REPLACEMENT CHARACTER `�`
This is not currently used by any expression, but may apply in future revisions.
_Option_ _identifiers_ and values are not included in the _fallback value_.
_Options_ and _attributes_ are not included in the _fallback value_.
_Pattern selection_ is not supported for _fallback values_.
Expand Down
52 changes: 52 additions & 0 deletions test/tests/fallback.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{
"$schema": "https://raw.githubusercontent.com/unicode-org/message-format-wg/main/test/schemas/v0/tests.schema.json",
"scenario": "Fallback",
"description": "Test cases for fallback behaviour.",
"defaultTestProperties": {
"bidiIsolation": "none",
"locale": "en-US",
"expErrors": true
},
"tests": [
{
"description": "function with unquoted literal operand",
"src": "{42 :test:function fails=format}",
"exp": "{|42|}"
},
{
"description": "function with quoted literal operand",
"src": "{|C:\\\\| :test:function fails=format}",
"exp": "{|C:\\\\|}"
},
{
"description": "unannotated implicit input variable",
"src": "{$var}",
"exp": "{$var}"
},
{
"description": "annotated implicit input variable",
"src": "{$var :number}",
"exp": "{$var}"
},
{
"description": "local variable with unknown function in declaration",
"src": ".local $var = {|val| :test:undefined} {{{$var}}}",
"exp": "{$var}"
},
{
"description": "function with local variable operand with unknown function in declaration",
"src": ".local $var = {|val| :test:undefined} {{{$var :test:function}}}",
"exp": "{$var}"
},
{
"description": "local variable with unknown function in placeholder",
"src": ".local $var = {|val|} {{{$var :test:undefined}}}",
"exp": "{$var}"
},
{
"description": "function with no operand",
"src": "{:test:undefined}",
"exp": "{:test:undefined}"
}
]
}
30 changes: 6 additions & 24 deletions test/tests/functions/number.json
Original file line number Diff line number Diff line change
Expand Up @@ -132,33 +132,15 @@
},
{
"src": ".local $foo = {$bar :number minimumFractionDigits=foo} {{bar {$foo}}}",
"params": [
{
"name": "bar",
"value": 4.2
}
],
"exp": "bar {$bar}",
"expErrors": [
{
"type": "bad-option"
}
]
"params": [{ "name": "bar", "value": 4.2 }],
"exp": "bar {$foo}",
"expErrors": [{ "type": "bad-option" }]
},
{
"src": ".local $foo = {$bar :number} {{bar {$foo}}}",
"params": [
{
"name": "bar",
"value": "foo"
}
],
"exp": "bar {$bar}",
"expErrors": [
{
"type": "bad-operand"
}
]
"params": [{ "name": "bar", "value": "foo" }],
"exp": "bar {$foo}",
"expErrors": [{ "type": "bad-operand" }]
},
{
"src": ".input {$foo :number} {{bar {$foo}}}",
Expand Down

0 comments on commit f3344b5

Please sign in to comment.