Repository navigation
Add GetAmountConvertToUnit definitions to both ECMA-262 and ECMA-402 - #110
Conversation
|
|
This looks good. Thanks! |
gibson042
left a comment
There was a problem hiding this comment.
Thanks! I have many suggestions, but this is an excellent starting point.
gibson042
left a comment
There was a problem hiding this comment.
I'd like to name the Records that are passed between operations, but won't insist on it being part of this PR. The rest of this review is a large handful of non-blocking suggestions.
fd51602 to
964a6d1
Compare
| 1. Let _requestedLocales_ be ? CanonicalizeLocaleList(_locale_). | ||
| 1. If _requestedLocales_ is an empty List, let _resolvedLocale_ be DefaultLocale(); else let _resolvedLocale_ be the first element of _requestedLocales_. |
There was a problem hiding this comment.
This should use actual locale negotiation rather than just picking the first element; again, see #95.
There was a problem hiding this comment.
Agree. I propose to punt that to a separate branch that tackles #95.
There was a problem hiding this comment.
Nope, for conversion every entry includes a 001 region, which means that the region of a valid locale will always be supported, and that therefore we will never fall back to a second-choice locale.
This does raise the question of whether we should have a region option instead of a locale option, as the data is region-dependent rather than language-dependent.
There was a problem hiding this comment.
I've tweaked things so that we use RegionPreference here, which also picks up region. In light of the fact taht every category and usage has a 001 entry, no requested locale can go unsupported, so I'm not sure I see how negotiation should go. I may be missing something. Perhaps file an issue to tackle this?
On region versus locale, I'd lean toward keeping locale. UTS 35 takes a locale as input, and it matches how Intl.Locale handles region-based data (calendars, hour cycles). That strikes me as sufficient, or do we need more here?
| 1. Let _unit_ be ~unset~. | ||
| 1. For each element _entry_ of _preferredUnits_, do | ||
| 1. Set _unit_ to _entry_.[[Unit]]. | ||
| 1. Let _convertedValue_ be ? ConvertUnitValue(_sourceValue_, _sourceUnit_, _unit_). |
There was a problem hiding this comment.
It's a bit awkward to specify O(n) ConvertUnitValue calls (n - 1 in SelectTargetUnit and the last in Amount.prototype.convertTo). I'm not objecting, but we should look for ways to improve that.
There was a problem hiding this comment.
Agree. Would you like to file a follow-on issue to track this?
Co-authored-by: Richard Gibson <richard.gibson@gmail.com>
Co-authored-by: Richard Gibson <richard.gibson@gmail.com>
Co-authored-by: Richard Gibson <richard.gibson@gmail.com>
Rename two leftover uses of _matchingElements_ to _matchingUnits_, declare GetPreferredUnits as returning a List of Records now that it can no longer throw, drop the ? at its call site accordingly, and add the missing closing asterisk in *"001"*.
Co-authored-by: Richard Gibson <richard.gibson@gmail.com>
The String branch of `.convertTo` collapsed "-0" to the mathematical value 0 before building the Number used for the conversion, so the sign got lost.
Move every observable property read into `SetAmountOptions` that ECMA-402 redefines with the help of a new `[[ExtendedOptions]]` field. `SetAmountOptions` also validates unit syntax as soon as the unit is read.
`GetOptionsObject` moved from ECMA-402 to ECMA-262.
gibson042
left a comment
There was a problem hiding this comment.
LGTM after a large handful of tweaks.
| so `amount.convertTo("milliliter")` is the same as `amount.convertTo({ unit: "milliliter" })`. | ||
| * `locale` (String or Array of Strings or undefined): | ||
| The locale for which the preferred unit of the corresponding category is determined. | ||
| Unit preferences depend only on the region of the locale, determined as in ECMA-402's [RegionPreference](https://tc39.es/ecma402/#sec-regionpreference): (1) the `rg` Unicode extension, if present ('US' for `en-GB-u-rg-uszzzz`), (2) the locale's region subtag (`GB` for `en-GB`), and finally (3) the most likely region for the locale (`US` for `en`). |
There was a problem hiding this comment.
| Unit preferences depend only on the region of the locale, determined as in ECMA-402's [RegionPreference](https://tc39.es/ecma402/#sec-regionpreference): (1) the `rg` Unicode extension, if present ('US' for `en-GB-u-rg-uszzzz`), (2) the locale's region subtag (`GB` for `en-GB`), and finally (3) the most likely region for the locale (`US` for `en`). | |
| Unit preferences depend only on the region of the locale, determined as in ECMA-402's [RegionPreference](https://tc39.es/ecma402/#sec-regionpreference): (1) the `rg` Unicode extension if present ("US" for `en-GB-u-rg-uszzzz`), (2) the locale's region subtag if present ("CH" for `de-CH`), the most likely region for the locale if known ("BR" for `pt`), and finally "001" (the World) as an ultimate fallback. |
| <h1>Amount Options Record</h1> | ||
| <p> | ||
| An <dfn variants="Amount Options Records">Amount Options Record</dfn> is a Record holding the resolved values of the options | ||
| accepted by the Amount constructor and by <emu-xref href="#sec-amount.prototype.convertto">Amount.prototype.convertTo</emu-xref>. |
There was a problem hiding this comment.
Simplification:
| accepted by the Amount constructor and by <emu-xref href="#sec-amount.prototype.convertto">Amount.prototype.convertTo</emu-xref>. | |
| accepted by the Amount constructor and by <emu-xref href="#sec-amount.prototype.convertto" title></emu-xref>. |
| 1. Set _roundingMode_ to ? GetOption(_opts_, *"roundingMode"*, ~string~, « *"ceil"*, *"floor"*, *"expand"*, *"trunc"*, *"halfCeil"*, *"halfFloor"*, *"halfExpand"*, *"halfTrunc"*, *"halfEven"* », *"halfEven"*). | ||
| 1. Set _significantDigits_ to ? GetOption(_opts_, *"significantDigits"*, ~number~, ~empty~, *undefined*). | ||
| 1. Let _unit_ be ? GetOption(_opts_, *"unit"*, ~string~, ~empty~, *undefined*). | ||
| 1. If ParseText(_opts_, |UnitIdentifier|) is not a Parse Node, throw a *RangeError* exception. |
There was a problem hiding this comment.
This may change based on discussion in next week's plenary, but I at least am advocating for
| 1. If ParseText(_opts_, |UnitIdentifier|) is not a Parse Node, throw a *RangeError* exception. | |
| 1. If ParseText(_opts_, |UnitIdentifier|) is not a Parse Node, throw a *SyntaxError* exception. |
| <p> | ||
| An ECMAScript implementation that includes the ECMA-402 Internationalization API | ||
| must implement this abstract operation as specified in the ECMA-402 specification. | ||
| If an ECMAScript implementation does not include the ECMA-402 API, | ||
| SetAmountOptions performs the following steps when called: | ||
| </p> |
There was a problem hiding this comment.
Nit:
| <p> | |
| An ECMAScript implementation that includes the ECMA-402 Internationalization API | |
| must implement this abstract operation as specified in the ECMA-402 specification. | |
| If an ECMAScript implementation does not include the ECMA-402 API, | |
| SetAmountOptions performs the following steps when called: | |
| </p> | |
| <p> | |
| An ECMAScript implementation that includes the ECMA-402 Internationalization API | |
| must implement this abstract operation as specified in ECMA-402. | |
| Otherwise, SetAmountOptions performs the following steps when called: | |
| </p> |
(mirroring e.g. AvailableNamedTimeZoneIdentifiers)
| SetAmountOptions ( | ||
| _target_: an Amount Options Record, | ||
| _source_: an Object, | ||
| ): either a normal completion containing ~unused~ or a throw completion |
There was a problem hiding this comment.
I think we want to read "locale" and "usage" for conversion but not construction, which means that distinction should be expressed in a parameter.
| SetAmountOptions ( | |
| _target_: an Amount Options Record, | |
| _source_: an Object, | |
| ): either a normal completion containing ~unused~ or a throw completion | |
| SetAmountOptions ( | |
| _target_: an Amount Options Record, | |
| _source_: an Object, | |
| _purpose_: ~construction~ or ~unit-conversion~, | |
| ): either a normal completion containing ~unused~ or a throw completion |
|
|
||
| <emu-clause id="sup-amount-options-record"> | ||
| <h1>Amount Options Record</h1> | ||
| <p>In an implementation that includes the ECMA-402 Internationalization API, the [[ExtendedOptions]] field of an <emu-xref href="#sec-amount-options-record">Amount Options Record</emu-xref> that has been populated by SetAmountOptions is a Record with fields [[Locale]] (an ECMAScript language value) and [[Usage]] (a String or *undefined*).</p> |
There was a problem hiding this comment.
| <p>In an implementation that includes the ECMA-402 Internationalization API, the [[ExtendedOptions]] field of an <emu-xref href="#sec-amount-options-record">Amount Options Record</emu-xref> that has been populated by SetAmountOptions is a Record with fields [[Locale]] (an ECMAScript language value) and [[Usage]] (a String or *undefined*).</p> | |
| <p>In an implementation that includes the ECMA-402 Internationalization API, the [[ExtendedOptions]] field of an <emu-xref href="#sec-amount-options-record">Amount Options Record</emu-xref> that has been populated by SetAmountOptions is a Record with fields [[Locales]] (a Language Priority List or *undefined*) and [[Usage]] (a String or *undefined*).</p> |
| 1. Let _locale_ be _extendedOpts_.[[Locale]]. | ||
| 1. Let _usage_ be _extendedOpts_.[[Usage]]. | ||
| 1. If _unit_ is not *undefined*, then | ||
| 1. If _usage_ is not *undefined* or _locale_ is not *undefined*, throw a *TypeError* exception. | ||
| 1. Return _unit_. | ||
| 1. If _usage_ is *undefined*, then | ||
| 1. If _locale_ is *undefined*, throw a *TypeError* exception. | ||
| 1. Set _usage_ to *"default"*. | ||
| 1. Let _sourceUnit_ be _conversionSource_.[[Unit]]. | ||
| 1. Let _sourceValue_ be GetAmountNumericValue(_conversionSource_). | ||
| 1. Let _category_ be ? GetUnitCategory(_sourceUnit_). | ||
| 1. Let _requestedLocales_ be ? CanonicalizeLocaleList(_locale_). | ||
| 1. If _requestedLocales_ is an empty List, let _resolvedLocale_ be DefaultLocale(); else let _resolvedLocale_ be the first element of _requestedLocales_. |
There was a problem hiding this comment.
| 1. Let _locale_ be _extendedOpts_.[[Locale]]. | |
| 1. Let _usage_ be _extendedOpts_.[[Usage]]. | |
| 1. If _unit_ is not *undefined*, then | |
| 1. If _usage_ is not *undefined* or _locale_ is not *undefined*, throw a *TypeError* exception. | |
| 1. Return _unit_. | |
| 1. If _usage_ is *undefined*, then | |
| 1. If _locale_ is *undefined*, throw a *TypeError* exception. | |
| 1. Set _usage_ to *"default"*. | |
| 1. Let _sourceUnit_ be _conversionSource_.[[Unit]]. | |
| 1. Let _sourceValue_ be GetAmountNumericValue(_conversionSource_). | |
| 1. Let _category_ be ? GetUnitCategory(_sourceUnit_). | |
| 1. Let _requestedLocales_ be ? CanonicalizeLocaleList(_locale_). | |
| 1. If _requestedLocales_ is an empty List, let _resolvedLocale_ be DefaultLocale(); else let _resolvedLocale_ be the first element of _requestedLocales_. | |
| 1. Let _requestedLocales_ be _extendedOpts_.[[Locales]]. | |
| 1. Let _usage_ be _extendedOpts_.[[Usage]]. | |
| 1. If _unit_ is not *undefined*, then | |
| 1. If _usage_ is not *undefined* or _requestedLocales_ is not *undefined*, throw a *TypeError* exception. | |
| 1. Return _unit_. | |
| 1. If _usage_ is *undefined*, then | |
| 1. If _requestedLocales_ is *undefined*, throw a *TypeError* exception. | |
| 1. Set _usage_ to *"default"*. | |
| 1. Let _sourceUnit_ be _conversionSource_.[[Unit]]. | |
| 1. Let _sourceValue_ be GetAmountNumericValue(_conversionSource_). | |
| 1. Let _category_ be ? GetUnitCategory(_sourceUnit_). | |
| 1. If _requestedLocales_ is an empty List, let _resolvedLocale_ be DefaultLocale(); else let _resolvedLocale_ be the first element of _requestedLocales_. |
Co-authored-by: Richard Gibson <richard.gibson@gmail.com>
Co-authored-by: Richard Gibson <richard.gibson@gmail.com>
Co-authored-by: Richard Gibson <richard.gibson@gmail.com>
Fixes #109 by adding an AO to ECMA-262 that's superseded by a redefinition in ECMA-402.
The actual extraction of preferred units from the CLDREdit: now included.units.xmlis left as a TODO.The preferences depend on unit category, usage, region, and value thresholds. The categories we get from validity/unit.xml, the region is calculated from the first-choice locale, and the preferences themselves are in supplemental/units.xml.
As the data always includes
001("the world") as one of the regions for each supported category + usage combination, no locale fallback is ever done during.convertTo(), as all well-formed locales are supported.