From f111102323c0a8fbdab68102b19339cb1f73f147 Mon Sep 17 00:00:00 2001 From: Eemeli Aro Date: Mon, 13 May 2024 12:42:53 -0400 Subject: [PATCH] Refactor errors, adding section for Message Function Errors (#774) * Refactor errors, adding section for Message Function Errors * Apply suggestions from code review Co-authored-by: Addison Phillips * Apply suggestions from code review * Drop exemplary text from Formatting Error description * Drop "Bad Expression", add SHOULD for letting functions pick their error * Use "appropriate" rather than "a" for Message Function Errors Co-authored-by: Addison Phillips * Apply suggestions from code review Co-authored-by: Richard Gibson Co-authored-by: Addison Phillips * Apply suggestions from code review Co-authored-by: Addison Phillips * Drop Selection & Formatting erorrs, add Bad Selector & Bad Variant Key * Apply suggestions from code review Co-authored-by: Addison Phillips --------- Co-authored-by: Addison Phillips Co-authored-by: Richard Gibson --- spec/README.md | 3 +- spec/errors.md | 149 +++++++++++++++++++++++++++------------------ spec/formatting.md | 12 ++-- spec/registry.md | 18 +++--- 4 files changed, 107 insertions(+), 75 deletions(-) diff --git a/spec/README.md b/spec/README.md index c9d364296..f35e7f487 100644 --- a/spec/README.md +++ b/spec/README.md @@ -15,8 +15,7 @@ 1. [Syntax Errors](errors.md#syntax-errors) 1. [Data Model Errors](errors.md#data-model-errors) 1. [Resolution Errors](errors.md#resolution-errors) - 1. [Selection Errors](errors.md#selection-errors) - 1. [Formatting Errors](errors.md#formatting-errors) + 1. [Message Function Errors](errors.md#message-function-errors) 1. [Registry](registry.md) 1. [`registry.dtd`](registry.dtd) 1. [Formatting](formatting.md) diff --git a/spec/errors.md b/spec/errors.md index 0fbac05bd..1fa78c8a2 100644 --- a/spec/errors.md +++ b/spec/errors.md @@ -16,12 +16,13 @@ and MUST be emitted as soon as possible. The other error categories are only emitted during formatting, but it might be possible to detect them with validation tools. -During selection, an _expression_ handler MUST only emit _Resolution Errors_ and _Selection Errors_. -During formatting, an _expression_ handler MUST only emit _Resolution Errors_ and _Formatting Errors_. +During selection and formatting, +_expression_ handlers MUST only emit _Message Function Errors_. -_Resolution Errors_ and _Formatting Errors_ in _expressions_ that are not used -in _pattern selection_ or _formatting_ MAY be ignored, -as they do not affect the output of the formatter. +Implementations do not have to check for or emit _Resolution Errors_ +or _Message Function Errors_ in _expressions_ that are not otherwise used by the _message_, +such as _placeholders_ in unselected _patterns_ +or _declarations_ that are never referenced during _formatting_. In all cases, when encountering a runtime error, a message formatter MUST provide some representation of the message. @@ -34,7 +35,7 @@ SHOULD prioritise _Syntax Errors_ and _Data Model Errors_ over others. When an error occurs within a _selector_, the _selector_ MUST NOT match any _variant_ _key_ other than the catch-all `*` -and a _Resolution Error_ or a _Selection Error_ MUST be emitted. +and a _Resolution Error_ or a _Message Function Error_ MUST be emitted. ## Syntax Errors @@ -242,34 +243,6 @@ or for private implementation use that is not supported by the current implement > * {{The value is not one.}} > ``` -### Invalid Expression - -An **_Invalid Expression_** error occurs when a _message_ includes an _expression_ -whose implementation-defined internal requirements produce an error during _function resolution_ -or when a _function_ returns a value (such as `null`) that the implementation does not support. - -An **_Operand Mismatch Error_** is an _Invalid Expression_ error that occurs when -an _operand_ provided to a _function_ during _function resolution_ does not match one of the -expected implementation-defined types for that function; -or in which a literal _operand_ value does not have the required format -and thus cannot be processed into one of the expected implementation-defined types -for that specific _function_. - -> For example, the following _message_ produces an _Operand Mismatch Error_ -> (a type of _Invalid Expression_ error) -> because the literal `|horse|` does not match the production `number-literal`, -> which is a requirement of the function `:number` for its operand: -> ``` -> .local $horse = {horse :number} -> {{You have a {$horse}.}} -> ``` -> The following _message_ might produce an _Invalid Expression_ error if the -> the function `:function` threw an exception or otherwise emitted an error -> rather than returning a valid value: ->``` -> {{This has an invalid expression {$var :function} because it has a bug in it.}} ->``` - ### Unsupported Statement An **_Unsupported Statement_** error occurs when a message includes a _reserved statement_. @@ -282,42 +255,41 @@ An **_Unsupported Statement_** error occurs when a message includes a > {{The message body}} > ``` -## Selection Errors +### Bad Selector -**_Selection Errors_** occur when message selection fails. +A **_Bad Selector_** error occurs when a message includes a _selector_ +with a resolved value which does not support selection. -> For example, attempting to format either of the following messages -> might result in a _Selection Error_ if done within a context that -> uses a `:number` selector function which requires its input to be numeric: -> -> ``` -> .match {|horse| :number} -> 1 {{The value is one.}} -> * {{The value is not one.}} -> ``` +> For example, attempting to format this message +> would result in a _Bad Selector_ error: > > ``` -> .local $sel = {|horse| :number} -> .match {$sel} -> 1 {{The value is one.}} -> * {{The value is not one.}} +> .local $day = {|2024-05-01| :date} +> .match {$day} +> * {{The due date is {$day}}} > ``` -## Formatting Errors +## Message Function Errors + +A **_Message Function Error_** is any error that occurs +when calling a message function implementation +or which depends on validation associated with a specific function. -**_Formatting Errors_** occur during the formatting of a resolved value, -for example when encountering a value with an unsupported type -or an internally inconsistent set of options. +Implementations SHOULD provide a way for _functions_ to emit +(or cause to be emitted) any of the types of error defined in this section. +Implementations MAY also provide implementation-defined _Message Function Error_ types. > For example, attempting to format any of the following messages -> might result in a _Formatting Error_ if done within a context that +> might result in a _Message Function Error_ if done within a context that > -> 1. provides for the variable reference `$user` to resolve to +> 1. Provides for the variable reference `$user` to resolve to > an object `{ name: 'Kat', id: 1234 }`, -> 2. provides for the variable reference `$field` to resolve to +> 2. Provides for the variable reference `$field` to resolve to > a string `'address'`, and -> 3. uses a `:get` formatting function which requires its argument to be an object and -> an option `field` to be provided with a string value, +> 3. Uses a `:get` message function which requires its argument to be an object and +> an option `field` to be provided with a string value. +> +> The exact type of _Message Function Error_ is determined by the message function implementation. > > ``` > Hello, {horse :get field=name}! @@ -336,3 +308,64 @@ or an internally inconsistent set of options. > Your {$field} is {$id :get field=$field} > ``` +### Bad Operand + +A **_Bad Operand_** error is any error that occurs due to the content or format of the _operand_, +such as when the _operand_ provided to a _function_ during _function resolution_ does not match one of the +expected implementation-defined types for that function; +or in which a literal _operand_ value does not have the required format +and thus cannot be processed into one of the expected implementation-defined types +for that specific _function_. + +> For example, the following _messages_ each produce a _Bad Operand_ error +> because the literal `|horse|` does not match the `number-literal` production, +> which is a requirement of the function `:number` for its operand: +> +> ``` +> .local $horse = {|horse| :number} +> {{You have a {$horse}.}} +> ``` +> +> ``` +> .match {|horse| :number} +> 1 {{The value is one.}} +> * {{The value is not one.}} +> ``` + +### Bad Option + +A **_Bad Option_** error is an error that occurs when there is +an implementation-defined error with an _option_ or its value. +These might include: +- A required _option_ is missing. +- Mutually exclusive _options_ are supplied. +- An _option_ value provided to a _function_ during _function resolution_ + does not match one of the implementation-defined types or values for that _function_; + or in which the literal _option_ value does not have the required format + and thus cannot be processed into one of the expected + implementation-defined types for that specific _function_. + +> For example, the following _message_ might produce a _Bad Option_ error +> because the literal `foo` does not match the production `digit-size-option`, +> which is a requirement of the function `:number` for its `minimumFractionDigits` _option_: +> +> ``` +> The answer is {42 :number minimumFractionDigits=foo}. +> ``` + +### Bad Variant Key + +A **_Bad Variant Key_** error is an error that occurs when a _variant_ _key_ +does not match the expected implementation-defined format. + +> For example, the following _message_ produces a _Bad Variant Key_ error +> because `horse` is not a recognized plural category and +> does not match the `number-literal` production, +> which is a requirement of the `:number` function: +> +> ``` +> .match {42 :number} +> 1 {{The value is one.}} +> horse {{The value is a horse.}} +> * {{The value is not one.}} +> ``` diff --git a/spec/formatting.md b/spec/formatting.md index 48235d4c5..6027175d3 100644 --- a/spec/formatting.md +++ b/spec/formatting.md @@ -250,8 +250,8 @@ the following steps are taken: argument type `T` and return type `U` for implementations of functions such that `U` can be coerced to `T`. - Implementations of a _function_ SHOULD emit an - _Invalid Expression_ error for _operands_ whose resolved value + Implementations of a _function_ SHOULD emit a + _Bad Operand_ error for _operands_ whose resolved value or type is not supported. > [!NOTE] @@ -307,13 +307,13 @@ the following steps are taken: resolve the value of the _expression_ as the result of that function call. If the call fails or does not return a valid value, - emit a _Invalid Expression_ error. + emit the appropriate _Message Function Error_ for the failure. Implementations MAY provide a mechanism for the _function_ to provide additional detail about internal failures. Specifically, if the cause of the failure was that the datatype, value, or format of the _operand_ did not match that expected by the _function_, - the _function_ might cause an _Operand Mismatch Error_ to be emitted. + the _function_ might cause a _Bad Operand_ error to be emitted. In all failure cases, use the _fallback value_ for the _expression_ as the resolved value. @@ -519,7 +519,7 @@ First, resolve the values of each _selector_: 1. Else: 1. Let `nomatch` be a resolved value for which selection always fails. 1. Append `nomatch` as the last element of the list `res`. - 1. Emit a _Selection Error_. + 1. Emit a _Bad Selector_ error. The form of the resolved values is determined by each implementation, along with the manner of determining their support for selection. @@ -735,7 +735,7 @@ each _text_ and _placeholder_ part of the selected _pattern_ is resolved and for Resolved values cannot always be formatted by a given implementation. When such an error occurs during _formatting_, -an implementation SHOULD emit a _Formatting Error_ and produce a +an implementation SHOULD emit an appropriate _Message Function Error_ and produce a _fallback value_ for the _placeholder_ that produced the error. A formatting function MAY substitute a value to use instead of a _fallback value_. diff --git a/spec/registry.md b/spec/registry.md index 19f167e31..b00a1aeab 100644 --- a/spec/registry.md +++ b/spec/registry.md @@ -297,7 +297,7 @@ The function `:string` provides string selection and formatting. The _operand_ of `:string` is either any implementation-defined type that is a string or for which conversion to a string is supported, or any _literal_ value. -All other values produce an _Invalid Expression_ error. +All other values produce a _Bad Operand_ error. > For example, in Java, implementations of the `java.lang.CharSequence` interface > (such as `java.lang.String` or `java.lang.StringBuilder`), @@ -609,7 +609,7 @@ The _function_ `:integer` performs selection as described in [Number Selection]( The _operand_ of a number function is either an implementation-defined type or a literal whose contents match the `number-literal` production in the [ABNF](/spec/message.abnf). -All other values produce an _Invalid Expression_ error. +All other values produce a _Bad Operand_ error. > For example, in Java, any subclass of `java.lang.Number` plus the primitive > types (`byte`, `short`, `int`, `long`, `float`, `double`, etc.) @@ -684,7 +684,7 @@ numeric selectors perform as described below. 1. Else if `key` is one of the keywords `zero`, `one`, `two`, `few`, `many`, or `other`, then 1. If `key` and `keyword` consist of the same sequence of Unicode code points, then 1. Append `key` as the last element of the list `resultKeyword`. - 1. Else, emit a _Selection Error_. + 1. Else, emit a _Bad Variant Key_ error. 1. Return a new list whose elements are the concatenation of the elements (in order) of `resultExact` followed by the elements (in order) of `resultKeyword`. > [!NOTE] @@ -785,7 +785,7 @@ If no options are specified, this function defaults to the following: The _operand_ of the `:datetime` function is either an implementation-defined date/time type or a _date/time literal value_, as defined in [Date and Time Operand](#date-and-time-operands). -All other _operand_ values produce an _Invalid Expression_ error. +All other _operand_ values produce a _Bad Operand_ error. #### Options @@ -793,7 +793,7 @@ The `:datetime` function can use either the appropriate _style options_ or can use a collection of _field options_ (but not both) to control the formatted output. -If both are specified, an _Invalid Expression_ error MUST be emitted +If both are specified, a _Bad Option_ error MUST be emitted and a _fallback value_ used as the resolved value of the _expression_. > [!NOTE] @@ -910,7 +910,7 @@ If no options are specified, this function defaults to the following: The _operand_ of the `:date` function is either an implementation-defined date/time type or a _date/time literal value_, as defined in [Date and Time Operand](#date-and-time-operands). -All other _operand_ values produce an _Invalid Expression_ error. +All other _operand_ values produce a _Bad Operand_ error. #### Options @@ -933,7 +933,7 @@ If no options are specified, this function defaults to the following: The _operand_ of the `:time` function is either an implementation-defined date/time type or a _date/time literal value_, as defined in [Date and Time Operand](#date-and-time-operands). -All other _operand_ values produce an _Invalid Expression_ error. +All other _operand_ values produce a _Bad Operand_ error. #### Options @@ -950,7 +950,7 @@ The function `:time` has these _options_: The _operand_ of a date/time function is either an implementation-defined date/time type or a _date/time literal value_, as defined below. -All other _operand_ values produce an _Invalid Expression_ error. +All other _operand_ values produce a _Bad Operand_ error. A **_date/time literal value_** is a non-empty string consisting of an ISO 8601 date, or an ISO 8601 datetime optionally followed by a timezone offset. @@ -972,7 +972,7 @@ For more information, see [Working with Timezones](https://w3c.github.io/timezon > The [ABNF](/spec/message.abnf) and [syntax](/spec/syntax.md) of MF2 > do not formally define date/time literals. > This means that a _message_ can be syntactically valid but produce -> an _Operand Mismatch Error_ at runtime. +> a _Bad Operand_ error at runtime. > [!NOTE] > String values passed as variables in the _formatting context_'s