diff --git a/intl.emu b/intl.emu index bda12cd4..7661d9a4 100644 --- a/intl.emu +++ b/intl.emu @@ -11,6 +11,172 @@

+ +

Abstract Operations of the Amount Object

+ + +

Unit Category Data

+

+ Unit category data is derived from CLDR file + validity/unit.xml. + As described in Unicode Technical Standard #35 Part 1 Core, Validity Data, + the body of the <id type='unit' idStatus='regular'> element contains + the supported units, represented as a whitespace-separated list in which each element is a sequence of subtags separated by *"-"* (U+002D HYPHEN-MINUS) characters. + For each unit, the first subtag provides the unit category, and the remaining subtags are the unit identifier. +

+
+ + +

Unit Preference Data

+

Unit preference data is derived from CLDR file supplemental/units.xml. As described in Unicode Technical Standard #35 Part 6 Supplemental, Unit Preferences Data, each <unitPreferences> element contains the preferred units for its category and usage. A <unitPreferences> element contains one or more <unitPreference> elements, which define the regions (a space-separated list of region identifiers) and optionally the geq threshold for the unit preference.

+
+ + +

+ GetAmountConvertToUnit ( + _sourceValue_: a Number, + _sourceUnit_: a String, + _options_: an Object, + _unitOption_: a String or *undefined*, + ): either a normal completion containing a String or a throw completion +

+
+
description
+
It determines the appropriate unit identifier to use as the conversion target.
+
redefinition
+
true
+
+

This definition supersedes the definition provided in .

+ + 1. Let _usage_ be ? GetOption(_options_, *"usage"*, ~string~, ~empty~, *undefined*). + 1. Let _locale_ be ? Get(_options_, *"locale"*). + 1. If _unitOption_ is a String, then + 1. If _usage_ is not *undefined* or _locale_ is not *undefined*, throw a *TypeError* exception. + 1. Return _unitOption_. + 1. If _usage_ is *undefined*, then + 1. If _locale_ is *undefined*, throw a *TypeError* exception. + 1. Set _usage_ to *"default"*. + 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 _maximalLocale_ be the result of the Add Likely Subtags algorithm applied to _resolvedLocale_. If an error is signaled, set _maximalLocale_ to _resolvedLocale_. + 1. Let _region_ be GetLocaleRegion(_maximalLocale_). + 1. If _region_ is *undefined*, set _region_ to *"001"*. + 1. Let _preferredUnits_ be ? GetPreferredUnits(_category_, _usage_, _region_). + 1. Let _entry_ be the first element of _preferredUnits_. + 1. Let _unit_ be _entry_.[[Unit]]. + 1. Repeat, while _preferredUnits_ has more than one element, + 1. Let _threshold_ be _entry_.[[Threshold]]. + 1. Let _convertedValue_ be ? ConvertUnitValue(_sourceValue_, _sourceUnit_, _unit_). + 1. If _convertedValue_ is not finite or abs(ℝ(_convertedValue_)) ≥ _threshold_, return _unit_. + 1. Remove the first element of _preferredUnits_. + 1. Set _entry_ to the first element of _preferredUnits_. + 1. Set _unit_ to _entry_.[[Unit]]. + 1. Return _unit_. + +
+ + +

+ GetUnitCategory ( + _unit_: a String, + ): either a normal completion containing a String or a throw completion +

+
+
description
+
It determines the CLDR unit category for _unit_ using the unit category data.
+
+ + 1. Let _unitTags_ be a List of Strings constructed by splitting by white space the body of the <id type='unit' idStatus='regular'> element of the unit category data and discarding any empty elements. + 1. For each element _tag_ of _unitTags_, do + 1. Let _i_ be StringIndexOf(_tag_, *"-"*, 0). + 1. Assert: _i_ is not ~not-found~ and _i_ > 0. + 1. Let _tagUnit_ be the substring of _tag_ from _i_ + 1. + 1. If SameValue(_unit_, _tagUnit_) is *true*, then + 1. Let _category_ be the substring of _tag_ from 0 to _i_. + 1. Return _category_. + 1. Throw a *RangeError* exception. + +
+ + +

+ GetPreferredUnits ( + _category_: a String, + _usage_: a String, + _region_: a String, + ): either a normal completion containing a List of Records or a throw completion +

+
+
description
+
It determines the preferred units to use for the given category, usage, and region, and returns them ordered by descending threshold.
+
+ + 1. Let _unitPreferenceElements_ be ? GetPreferredUnitElements(_category_, _usage_). + 1. Assert: _region_ can be matched by the unicode_region_id Unicode locale nonterminal. + 1. Set _region_ to the ASCII-uppercase of _region_. + 1. Let _fallback_ be an empty List. + 1. Let _match_ be an empty List. + 1. For each element _unitPreference_ of _unitPreferenceElements_, do + 1. Assert: _unitPreference_ has an attribute regions. + 1. Let _regionsAttr_ be the String value of the regions attribute of _unitPreference_. + 1. Let _regions_ be StringSplitToList(_regionsAttr_, *" "*). + 1. Let _geq_ be the mathematical value of the geq attribute of _unitPreference_ if present, or 1 otherwise. + 1. Let _unit_ be the String value of the body of the _unitPreference_ element. + 1. If _regions_ contains _region_, append the Record { [[Unit]]: _unit_, [[Threshold]]: _geq_ } to _match_. + 1. Else if _regions_ contains *"001"*, append the Record { [[Unit]]: _unit_, [[Threshold]]: _geq_ } to _fallback_. + 1. If _match_ is not empty, return _match_. + 1. Assert: _fallback_ is not empty. + 1. Return _fallback_. + + +

Unicode Technical Standard #35 Part 1, Unit Preferences Constraints specifies that the unit preference data for a given category, usage, and region-set is already in descending order [by threshold].

+
+
+ + +

+ GetPreferredUnitElements ( + _category_: a String, + _usage_: a String, + ): either a normal completion containing a List or a throw completion +

+
+
description
+
It determines the <unitPreference> elements in the unit preference data that match the given category and usage.
+
+ + 1. Repeat, + 1. If _category_ is the category and _usage_ is the usage of a <unitPreferences> element in the unit preference data, then + 1. Let _unitPreferencesParent_ be that element. + 1. Let _unitPreferenceElements_ be the List of <unitPreference> elements in _unitPreferencesParent_. + 1. Return _unitPreferenceElements_. + 1. Else if _usage_ is not *"default"*, then + 1. Let _len_ be the length of _usage_. + 1. Let _idx_ be StringLastIndexOf(_usage_, *"-"*, _len_ - 1). + 1. If _idx_ is ~not-found~ or _idx_ = 0, set _usage_ to *"default"*; else set _usage_ to the substring of _usage_ from 0 to _idx_. + 1. Else, + 1. Throw a *RangeError* exception. + + +

+ This abstract operation implements the first two steps of the + Compute the preferred output unit + algorithm specified in + Unicode Technical Standard #35 Part 6, Supplemental, Unit Preferences. +

+
+ +

+ TODO: The UTS #35 algorithm includes fallbacking for regions based on + UN M.49 territory containment, + but the unit preference data does not (yet) include any other numeric regions than 001. + We'll probably need to implement that in order to be compatible with possible future CLDR updates. +

+
+
+
+

Properties of the Amount Prototype Object

@@ -67,8 +233,10 @@

NumberFormat Objects

+

Abstract Operations for NumberFormat Objects

+

GetNumberFormatPattern ( @@ -202,7 +370,7 @@ 1. Set _intlObj_.[[CurrencyDisplay]] to _currencyDisplay_. 1. Set _intlObj_.[[CurrencySign]] to _currencySign_. 1. If _style_ is *"unit"*, then - 1. If _unit_ is not *undefined*, Set set _intlObj_.[[Unit]] to _unit_. + 1. If _unit_ is not *undefined*, Set set _intlObj_.[[Unit]] to _unit_. 1. Set _intlObj_.[[UnitDisplay]] to _unitDisplay_. 1. Return ~unused~. diff --git a/spec.emu b/spec.emu index 5febed1b..fa757d39 100644 --- a/spec.emu +++ b/spec.emu @@ -76,15 +76,15 @@ location: https://github.com/tc39/proposal-amount/

GetAmountOptions ( - _opts_: an Object + _options_: an ECMAScript language value, ): either a normal completion containing a Record with fields [[FractionDigits]] (a non-negative integer or *undefined*), [[RoundingMode]] (a rounding mode), [[SignificantDigits]] (a positive integer or *undefined*), and [[Unit]] (a String or *undefined*) or a throw completion

description
-
It validates the given _options_ (an ECMAScript object) for creating an Amount and returns a Record with slots set to appropriate marthematical values (or *undefined*).
+
It validates the given _options_ for creating an Amount and returns a Record with slots set to appropriate marthematical values (or *undefined*).
- 1. Let _opts_ be ? GetOptionsObject(_opts_). + 1. Let _opts_ be ? GetOptionsObject(_options_). 1. Let _fractionDigits_ be ? GetOption(_opts_, *"fractionDigits"*, ~number~, ~empty~, *undefined*). 1. Let _roundingMode_ be ? GetOption(_opts_, *"roundingMode"*, ~string~, « *"ceil"*, *"floor"*, *"expand"*, *"trunc"*, *"halfCeil"*, *"halfFloor"*, *"halfExpand"*, *"halfTrunc"*, *"halfEven"* », *"halfEven"*). 1. Let _significantDigits_ be ? GetOption(_opts_, *"significantDigits"*, ~number~, ~empty~, *undefined*). @@ -105,30 +105,47 @@ location: https://github.com/tc39/proposal-amount/

GetAmountConvertToOptions ( - _opts_: an Object + _sourceValue_: a Number, + _sourceUnit_: a String, + _options_: an ECMAScript language value, ): either a normal completion containing a Record with fields [[FractionDigits]] (a non-negative integer or *undefined*), [[RoundingMode]] (a rounding mode), [[SignificantDigits]] (a positive integer or *undefined*), and [[Unit]] (a String or *undefined*) or a throw completion

description
-
It validates the given _options_ (an ECMAScript object) for converting an Amount to another Amount and returns a Record with slots set to appropriate marthematical values (or *undefined*).
+
It validates the given _options_ for converting an Amount to another Amount and returns a Record with slots set to appropriate marthematical values (or *undefined*).
- 1. Let _opts_ be ? GetOptionsObject(_opts_). - 1. Let _fractionDigits_ be ? GetOption(_opts_, *"fractionDigits"*, ~number~, ~empty~, *undefined*). - 1. Let _roundingMode_ be ? GetOption(_opts_, *"roundingMode"*, ~string~, « *"ceil"*, *"floor"*, *"expand"*, *"trunc"*, *"halfCeil"*, *"halfFloor"*, *"halfExpand"*, *"halfTrunc"*, *"halfEven"* », *"halfEven"*). - 1. Let _significantDigits_ be ? GetOption(_opts_, *"significantDigits"*, ~number~, ~empty~, *undefined*). - 1. Let _unit_ be ? GetOption(_opts_, *"unit"*, ~string~, ~empty~, *undefined*). - 1. If _fractionDigits_ is not *undefined*, then - 1. If _significantDigits_ is not *undefined*, throw a *RangeError* exception. - 1. If _fractionDigits_ is not an integral Number, throw a *RangeError* exception. - 1. Set _fractionDigits_ to ℝ(_fractionDigits_). - 1. If _fractionDigits_ is not in the inclusive interval from 0 to 100, throw a *RangeError* exception. - 1. Else if _significantDigits_ is not *undefined*, then - 1. If _significantDigits_ is not an integral Number, throw a *RangeError* exception. - 1. Set _significantDigits_ to ℝ(_significantDigits_). - 1. If _significantDigits_ is not in the inclusive interval from 1 to 21, throw a *RangeError* exception. - 1. If _unit_ is the empty String, throw a *RangeError* exception. - 1. Return the Record { [[FractionDigits]]: _fractionDigits_, [[RoundingMode]]: _roundingMode_, [[SignificantDigits]]: _significantDigits_, [[Unit]]: _unit_ }. + 1. If _options_ is not an Object, throw a *TypeError* exception. + 1. Let _resolvedOpts_ be ? GetAmountOptions(_options_). + 1. Let _unitOption_ be _resolvedOpts_.[[Unit]]. + 1. Let _unit_ be ? GetAmountConvertToUnit(_sourceValue_, _sourceUnit_, _options_, _unitOption_). + 1. Set _resolvedOpts_.[[Unit]] to _unit_. + 1. Return _resolvedOpts_. + +
+ + +

+ GetAmountConvertToUnit ( + _sourceValue_: a Number, + _sourceUnit_: a String, + _options_: an Object, + _unitOption_: a String or *undefined*, + ): either a normal completion containing a String or a throw completion +

+
+
description
+
It determines the appropriate unit identifier to use as the conversion target.
+
+

+ An ECMAScript implementation that includes the ECMA-402 Internationalization API + must implement this method as specified in the ECMA-402 specification. + If an ECMAScript implementation does not include the ECMA-402 API, + GetAmountConvertToUnit performs the following steps when called: +

+ + 1. If _unitOption_ is *undefined*, throw a *TypeError* exception. + 1. Return _unitOption_.
@@ -182,7 +199,7 @@ location: https://github.com/tc39/proposal-amount/

Unit Conversion Data

-

Unit conversion data is derived from CLDR file units.xml. As described in Unicode Technical Standard #35 Part 6 Supplemental, Conversion Data, each <convertUnit> element defines how to convert a source unit into a compatible baseUnit. An ECMAScript implementation must ignore all special conversions and support all conversions based on factor and/or offset, interpreting the value for each as an arithmetic expression with mathematical value operands (noting the respective defaults of 1 and 0 and the implicit presence of an identity mapping for each unit identified as the value of a baseUnit).

+

Unit conversion data is derived from CLDR file supplemental/units.xml. As described in Unicode Technical Standard #35 Part 6 Supplemental, Conversion Data, each <convertUnit> element defines how to convert a source unit into a compatible baseUnit. An ECMAScript implementation must ignore all special conversions and support all conversions based on factor and/or offset, interpreting the value for each as an arithmetic expression with mathematical value operands (noting the respective defaults of 1 and 0 and the implicit presence of an identity mapping for each unit identified as the value of a baseUnit).

Two units are convertible if and only if they share the same baseUnit value in CLDR. A unit that appears as a baseUnit value has an implicit identity conversion (factor 1, offset 0).

In factor and offset expressions, * multiplication binds more tightly than / division, and constants defined by <unitConstant> elements are valid operands.

@@ -434,14 +451,6 @@ location: https://github.com/tc39/proposal-amount/ 1. Let _O_ be the *this* value. 1. Perform ? RequireInternalSlot(_O_, [[AmountValue]]). - 1. Let _sourceUnit_ be _O_.[[Unit]]. - 1. If _sourceUnit_ is *undefined*, throw a *TypeError* exception. - 1. Let _validatedOpts_ be ? GetAmountConvertToOptions(_options_). - 1. Let _targetUnit_ be _validatedOpts_.[[Unit]]. - 1. If _targetUnit_ is *undefined*, throw a *TypeError* exception. - 1. Let _roundingMode_ be _validatedOpts_.[[RoundingMode]]. - 1. Let _fractionDigits_ be _validatedOpts_.[[FractionDigits]]. - 1. Let _significantDigits_ be _validatedOpts_.[[SignificantDigits]]. 1. Let _v_ be _O_.[[AmountValue]]. 1. If _v_ is a Number, then 1. Let _sourceValue_ be _v_. @@ -450,9 +459,16 @@ location: https://github.com/tc39/proposal-amount/ 1. Else, 1. Assert: _v_ is a String. 1. Let _sourceValue_ be StringToNumber(_v_). + 1. Let _sourceUnit_ be _O_.[[Unit]]. + 1. If _sourceUnit_ is *undefined*, throw a *TypeError* exception. + 1. Let _opts_ be ? GetAmountConvertToOptions(_sourceValue_, _sourceUnit_, _options_). + 1. Let _targetUnit_ be _opts_.[[Unit]]. 1. Let _convertedValue_ be ? ConvertUnitValue(_sourceValue_, _sourceUnit_, _targetUnit_). 1. Let _result_ be OrdinaryObjectCreate(%Amount.prototype%, « [[AmountValue]], [[Unit]] »). + 1. Let _fractionDigits_ be _opts_.[[FractionDigits]]. + 1. Let _significantDigits_ be _opts_.[[SignificantDigits]]. 1. If _convertedValue_ is finite and (_fractionDigits_ is not *undefined* or _significantDigits_ is not *undefined*), then + 1. Let _roundingMode_ be _opts_.[[RoundingMode]]. 1. Let _formatter_ be CreateFormatterObject(_roundingMode_, _fractionDigits_, _fractionDigits_, _significantDigits_, _significantDigits_, *undefined*). 1. Let _formatted_ be FormatNumericToString(_formatter_, ℝ(_convertedValue_), 0). 1. Let _formattedMV_ be ! ToIntlMathematicalValue(_formatted_.[[FormattedString]]).