-
Notifications
You must be signed in to change notification settings - Fork 106
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[4th Edition] NumberFormat.prototype.formatToParts() #79
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -104,54 +104,100 @@ <h1>Number Format Functions</h1> | |
</p> | ||
</emu-clause> | ||
|
||
<emu-clause id="sec-formatnumber" aoid="FormatNumber"> | ||
<h1>FormatNumber(numberFormat, x)</h1> | ||
<emu-clause id="sec-partitionnumberpattern" aoid="PartitionNumberPattern"> | ||
<h1>PartitionNumberPattern(numberFormat, x)</h1> | ||
|
||
<p> | ||
When the FormatNumber abstract operation is called with arguments _numberFormat_ (which must be an object initialized as a NumberFormat) and _x_ (which must be a Number value), it returns a String value representing _x_ according to the effective locale and the formatting options of _numberFormat_. This abstract operation functions as follows: | ||
The *PartitionNumberPattern* abstract operation is called with arguments _numberFormat_ (which must be an object initialized as a NumberFormat) and _x_ (which must be a Number value), interprets _x_ as a numeric value, and creates the corresponding parts according to the effective locale and the formatting options of _numberFormat_. The following steps are taken: | ||
</p> | ||
|
||
<emu-alg> | ||
1. Let _negative_ be *false*. | ||
1. If the _result_ of isFinite(_x_) is *false*, then | ||
1. If _x_ is *NaN*, | ||
1. Let _n_ be an ILD String value indicating the *NaN* value. | ||
1. Else, | ||
1. Let _n_ be an ILD String value indicating infinity. | ||
1. If _x_ < 0, let _negative_ be *true*. | ||
1. If _x_ is not *NaN* and _x_ < 0, then: | ||
1. Let _x_ be -_x_. | ||
1. Let _pattern_ be the value of _numberFormat_.[[negativePattern]]. | ||
1. Else, | ||
1. If _x_ < 0, then | ||
1. Let _negative_ be *true*. | ||
1. Let _x_ be -_x_. | ||
1. If the value of _numberFormat_.[[style]] is *"percent"*, let _x_ be 100 × _x_. | ||
1. If the _numberFormat_.[[minimumSignificantDigits]] and _numberFormat_.[[maximumSignificantDigits]] are present, then | ||
1. Let _n_ be ToRawPrecision(_x_, _numberFormat_.[[minimumSignificantDigits]], _numberFormat_.[[maximumSignificantDigits]]). | ||
1. Let _pattern_ be the value of _numberFormat_.[[positivePattern]]. | ||
1. Let _result_ be a new empty List. | ||
1. Let _beginIndex_ be Call(*%StringProto_indexOf%*, _pattern_, *"{"*, *0*). | ||
1. Let _endIndex_ be 0. | ||
1. Let _nextIndex_ be 0. | ||
1. Let _length_ be the number of code units in _pattern_. | ||
1. Repeat while _beginIndex_ is an integer index into _pattern_: | ||
1. Set _endIndex_ to Call(*%StringProto_indexOf%*, _pattern_, *"}"*, _beginIndex_) | ||
1. If _endIndex_ = -1, throw new Error exception. | ||
1. If _beginIndex_ is greater than _nextIndex_, then: | ||
1. Let _literal_ be a substring of _pattern_ from position _nextIndex_, inclusive, to position _beginIndex_, exclusive. | ||
1. Add new part record { [[type]]: *"literal"*, [[value]]: _literal_ } as a new element of the list _result_. | ||
1. Let _p_ be the substring of _pattern_ from position _beginIndex_, exclusive, to position _endIndex_, exclusive. | ||
1. If _p_ is equal *"number"*, then: | ||
1. If _x_ is *NaN*, | ||
1. Let _n_ be an ILD String value indicating the *NaN* value. | ||
1. Add new part record { [[type]]: *"nan"*, [[value]]: _n_ } as a new element of the list _result_. | ||
1. Else if isFinite(_x_) is *false*, | ||
1. Let _n_ be an ILD String value indicating infinity. | ||
1. Add new part record { [[type]]: *"infinity"*, [[value]]: _n_ } as a new element of the list _result_. | ||
1. Else, | ||
1. If the value of _numberFormat_.[[style]] is *"percent"*, let _x_ be 100 × _x_. | ||
1. If the _numberFormat_.[[minimumSignificantDigits]] and _numberFormat_.[[maximumSignificantDigits]] are present, then | ||
1. Let _n_ be ToRawPrecision(_x_, _numberFormat_.[[minimumSignificantDigits]], _numberFormat_.[[maximumSignificantDigits]]). | ||
1. Else, | ||
1. Let _n_ be ToRawFixed(_x_, _numberFormat_.[[minimumIntegerDigits]], _numberFormat_.[[minimumFractionDigits]], _numberFormat_.[[maximumFractionDigits]]). | ||
1. If the value of the _numberFormat_.[[numberingSystem]] matches one of the values in the "Numbering System" column of <emu-xref href="#table-numbering-system-digits"></emu-xref> below, then | ||
1. Let _digits_ be an array whose 10 String valued elements are the UTF-16 string representations of the 10 _digits_ specified in the "Digits" column of the matching row in <emu-xref href="#table-numbering-system-digits"></emu-xref>. | ||
1. Replace each _digit_ in _n_ with the value of _digits_[_digit_]. | ||
1. Else use an implementation dependent algorithm to map _n_ to the appropriate representation of _n_ in the given numbering system. | ||
1. Let _decimalSepIndex_ be Call(*%StringProto_indexOf%*, _n_, *"."*, 0). | ||
1. If _decimalSepIndex_ > 0, then: | ||
1. Let _integer_ be the substring of _n_ from position 0, inclusive, to position _decimalSepIndex_, exclusive. | ||
1. Let _fraction_ be the substring of _n_ from position _decimalSepIndex_, exclusive, to the end of _n_. | ||
1. Else: | ||
1. Let _integer_ be _n_. | ||
1. Let _fraction_ be *undefined*. | ||
1. If the value of the _numberFormat_.[[useGrouping]] is *true*, | ||
1. Let _groupSepSymbol_ be the ILND String representing the grouping separator. | ||
1. Let _groups_ be a List whose elements are, in left to right order, the substrings defined by ILND set of locations within the _integer_. | ||
1. Assert: The number of elements in _groups_ List is greater than *0*. | ||
1. Repeat, while _groups_ List is not empty: | ||
1. Remove the first element from _groups_ and let _integerGroup_ be the value of that element. | ||
1. Add new part record { [[type]]: *"integer"*, [[value]]: _integerGroup_ } as a new element of the list _result_. | ||
1. If _groups_ List is not empty, then: | ||
1. Add new part record { [[type]]: *"group"*, [[value]]: _groupSepSymbol_ } as a new element of the list _result_. | ||
1. Else, | ||
1. Add new part record { [[type]]: *"integer"*, [[value]]: _integer_ } as a new element of the list _result_. | ||
1. If _fraction_ is not *undefined*, then: | ||
1. Let _decimalSepSymbol_ be the ILND String representing the decimal separator. | ||
1. Add new part record { [[type]]: *"decimal"*, [[value]]: _decimalSepSymbol_ } as a new element of the list _result_. | ||
1. Add new part record { [[type]]: *"fraction"*, [[value]]: _fraction_ } as a new element of the list _result_. | ||
1. Else if _p_ is equal *"plusSign"*, then: | ||
1. Let _plusSignSymbol_ be the ILND String representing the plus sign. | ||
1. Add new part record { [[type]]: *"plusSign"*, [[value]]: _plusSignSymbol_ } as a new element of the list _result_. | ||
1. Else if _p_ is equal *"minusSign"*, then: | ||
1. Let _minusSignSymbol_ be the ILND String representing the minus sign. | ||
1. Add new part record { [[type]]: *"minusSign"*, [[value]]: _minusSignSymbol_ } as a new element of the list _result_. | ||
1. Else if _p_ is equal *"percentSign"* and _numberFormat_.[[style]] is *"percent"*, then: | ||
1. Let _percentSignSymbol_ be the ILND String representing the percent sign. | ||
1. Add new part record { [[type]]: *"percentSign"*, [[value]]: _percentSignSymbol_ } as a new element of the list _result_. | ||
1. Else if _p_ is equal *"currency"* and _numberFormat_.[[style]] is *"currency"*, then: | ||
1. Let _currency_ be the value of _numberFormat_.[[currency]]. | ||
1. Assert: _numberFormat_.[[currencyDisplay]] is *"code"*, *"symbol"* or *"name"*. | ||
1. If _numberFormat_.[[currencyDisplay]] is *"code"*, then | ||
1. Let _cd_ be _currency_. | ||
1. Else if _numberFormat_.[[currencyDisplay]] is *"symbol"*, then | ||
1. Let _cd_ be an ILD string representing _currency_ in short form. If the implementation does not have such a representation of _currency_, use _currency_ itself. | ||
1. Else if _numberFormat_.[[currencyDisplay]] is *"name"*, then | ||
1. Let _cd_ be an ILD string representing _currency_ in long form. If the implementation does not have such a representation of _currency_, then use _currency_ itself. | ||
1. Add new part record { [[type]]: *"currency"*, [[value]]: _cd_ } as a new element of the list _result_. | ||
1. Else, | ||
1. Let _n_ be ToRawFixed(_x_, _numberFormat_.[[minimumIntegerDigits]], _numberFormat_.[[minimumFractionDigits]], _numberFormat_.[[maximumFractionDigits]]). | ||
1. If the value of the _numberFormat_.[[numberingSystem]] matches one of the values in the "Numbering System" column of <emu-xref href="#table-numbering-system-digits"></emu-xref> below, then | ||
1. Let _digits_ be an array whose 10 String valued elements are the UTF-16 string representations of the 10 _digits_ specified in the "Digits" column of the matching row in <emu-xref href="#table-numbering-system-digits"></emu-xref>. | ||
1. Replace each _digit_ in _n_ with the value of _digits_[_digit_]. | ||
1. Else use an implementation dependent algorithm to map _n_ to the appropriate representation of _n_ in the given numbering system. | ||
1. If _n_ contains the character *"."*, replace it with an ILND String representing the decimal separator. | ||
1. If the value of the _numberFormat_.[[useGrouping]] is *true*, insert an ILND String representing a grouping separator into an ILND set of locations within the integer part of _n_. | ||
1. If _negative_ is *true*, then | ||
1. Let _result_ be the value of _numberFormat_.[[negativePattern]]. | ||
1. Else, | ||
1. Let _result_ be the value of _numberFormat_.[[positivePattern]]. | ||
1. Replace the substring *"{number}"* within _result_ with _n_. | ||
1. If the value of the _numberFormat_.[[style]] is *"currency"*, then | ||
1. Let _currency_ be the value of _numberFormat_.[[currency]]. | ||
1. If _numberFormat_.[[currencyDisplay]] is *"code"*, then | ||
1. Let _cd_ be _currency_. | ||
1. Else if _numberFormat_.[[currencyDisplay]] is *"symbol"*, then | ||
1. Let _cd_ be an ILD string representing _currency_ in short form. If the implementation does not have such a representation of _currency_, use _currency_ itself. | ||
1. Else if _numberFormat_.[[currencyDisplay]] is *"name"*, then | ||
1. Let _cd_ be an ILD string representing _currency_ in long form. If the implementation does not have such a representation of _currency_, then use _currency_ itself. | ||
1. Replace the substring *"{currency}"* within _result_ with _cd_. | ||
1. Let _literal_ be the substring of _pattern_ from position _beginIndex_, inclusive, to position _endIndex_, inclusive. | ||
1. Add new part record { [[type]]: *"literal"*, [[value]]: _literal_ } as a new element of the list _result_. | ||
1. Set _nextIndex_ to _endIndex_ + 1. | ||
1. Set _beginIndex_ to Call(*%StringProto_indexOf%*, _pattern_, "{", _nextIndex_) | ||
1. If _nextIndex_ is less than _length_, then: | ||
1. Let _literal_ be the substring of _pattern_ from position _nextIndex_, inclusive, to position _length_, exclusive. | ||
1. Add new part record { [[type]]: *"literal"*, [[value]]: _literal_ } as a new element of the list _result_. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should use |
||
1. Return _result_. | ||
</emu-alg> | ||
|
||
|
||
<emu-table id="table-numbering-system-digits"> | ||
<emu-caption>Numbering systems with simple digit mappings</emu-caption> | ||
<table class="real-table"> | ||
|
@@ -257,10 +303,47 @@ <h1>FormatNumber(numberFormat, x)</h1> | |
</emu-note> | ||
|
||
<emu-note> | ||
It is recommended that implementations use the locale data provided by the Common Locale Data Repository (available at <a href="http://cldr.unicode.org/">http://cldr.unicode.org/</a>). | ||
It is recommended that implementations use the locale provided by the Common Locale Data Repository (available at <a href="http://cldr.unicode.org/">http://cldr.unicode.org/</a>). | ||
</emu-note> | ||
</emu-clause> | ||
|
||
<emu-clause id="sec-formatnumber" aoid="FormatNumber"> | ||
<h1>FormatNumber(numberFormat, x)</h1> | ||
|
||
<p> | ||
The FormatNumber abstract operation is called with arguments _numberFormat_ (which must be an object initialized as a NumberFormat) and _x_ (which must be a Number value), and performs the following steps: | ||
</p> | ||
|
||
<emu-alg> | ||
1. Let _parts_ be ? PartitionNumberPattern(_numberFormat_, _x_). | ||
1. Let _result_ be an empty String. | ||
1. For each _part_ in _parts_, do: | ||
1. Set _result_ to a String value produced by concatenating _result_ and _part_.[[value]]. | ||
1. Return _result_. | ||
</emu-alg> | ||
</emu-clause> | ||
|
||
<emu-clause id="sec-formatnumbertoparts" aoid="FormatNumberToParts"> | ||
<h1>FormatNumberToParts(numberFormat, x)</h1> | ||
|
||
<p> | ||
The FormatNumberToParts abstract operation is called with arguments _numberFormat_ (which must be an object initialized as a NumberFormat) and _x_ (which must be a Number value), and performs the following steps: | ||
</p> | ||
|
||
<emu-alg> | ||
1. Let _parts_ be ? PartitionNumberPattern(_numberFormat_, _x_). | ||
1. Let _result_ be ArrayCreate(0). | ||
1. Let _n_ be 0. | ||
1. For each _part_ in _parts_, do: | ||
1. Let _O_ be ObjectCreate(%ObjectPrototype%). | ||
1. Perform ? CreateDataPropertyOrThrow(_O_, "type", _part_.[[type]]). | ||
1. Perform ? CreateDataPropertyOrThrow(_O_, "value", _part_.[[value]]). | ||
1. Perform ? CreateDataPropertyOrThrow(_result_, ? ToString(_n_), _O_). | ||
1. Increment _n_ by 1. | ||
1. Return _result_. | ||
</emu-alg> | ||
</emu-clause> | ||
|
||
<emu-clause id="sec-torawprecision" aoid="ToRawPrecision"> | ||
<h1>ToRawPrecision(x, minPrecision, maxPrecision)</h1> | ||
|
||
|
@@ -409,7 +492,7 @@ <h1>Internal slots</h1> | |
|
||
<ul> | ||
<li>The array that is the value of the "nu" property of any locale property of [[localeData]] must not include the values "native", "traditio", or "finance".</li> | ||
<li>[[localeData]][locale] must have a patterns property for all locale values. The value of this property must be an object, which must have properties with the names of the three number format styles: *"decimal"*, *"percent"*, and *"currency"*. Each of these properties in turn must be an object with the properties positivePattern and negativePattern. The value of these properties must be string values that contain a substring *"{number}"*; the values within the currency property must also contain a substring *"{currency}"*. The pattern strings must not contain any characters in the General Category “Number, decimal digit" as specified by the Unicode Standard.</li> | ||
<li>[[localeData]][locale] must have a patterns property for all locale values. The value of this property must be an object, which must have properties with the names of the three number format styles: *"decimal"*, *"percent"*, and *"currency"*. Each of these properties in turn must be an object with the properties positivePattern and negativePattern. The value of these properties must be string values that must contain the substring *"{number}"* and may contain the substrings *"{plusSign}"*, and *"{minusSign}"*; the values within the percent property must also contain the substring *"{percentSign}"*; the values within the currency property must also contain the substring *"{currency}"*. The pattern strings must not contain any characters in the General Category “Number, decimal digit" as specified by the Unicode Standard.</li> | ||
</ul> | ||
|
||
<emu-note> | ||
|
@@ -457,16 +540,33 @@ <h1>get Intl.NumberFormat.prototype.format</h1> | |
<emu-alg> | ||
1. Let _nf_ be *this* value. | ||
1. If Type(_nf_) is not Object, throw a *TypeError* exception. | ||
1. If _nf_ does not have an [[initializedNumberFormat]] internal slot, throw a *TypeError* exception. | ||
1. If _nf_.[[boundFormat]] is *undefined*, then | ||
1. If _nf_ does not have an [[initializedNumberFormat]] internal slot whose value is *true*, throw a *TypeError* exception. | ||
1. If the [[boundFormat]] internal slot of _nf_ is *undefined*, then | ||
1. Let _F_ be a new built-in function object as defined in Number Format Functions (<emu-xref href="#sec-number-format-functions"></emu-xref>). | ||
1. Let _bf_ be BoundFunctionCreate(_F_, _nf_, « »). | ||
1. Perform ! DefinePropertyOrThrow(_bf_, `"length"`, PropertyDescriptor {[[Value]]: 1, [[Writable]]: *false*, [[Enumerable]]: *false*, [[Configurable]]: *true*}). | ||
1. Set _nf_.[[boundFormat]] to bf. | ||
1. Set _nf_.[[boundFormat]] to _bf_. | ||
1. Return _nf_.[[boundFormat]]. | ||
</emu-alg> | ||
</emu-clause> | ||
|
||
<emu-clause id="sec-intl.numberformat.prototype.formattoparts"> | ||
<h1>Intl.NumberFormat.prototype.formatToParts ([ value ])</h1> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Making the There's a somewhat-understandable default value that can be applied for dates, namely the current date/time. But there's not really a good default value for numbers. You could argue for it being zero, but wouldn't it be more sensible for the user to pass I think you should make the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hey @jswalden, IIRC, the decision was based on the idea to align with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @caridy This wouldn't change any logic -- it would simply change the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think I agree with @jswalden here. The current default behavior doesn't make much sense (NaN), so I'm ok with requiring an argument. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @zbraniecki I don't think that's what @jswalden is proposing. He is just proposing to set the length to 1, but keeping value as optional. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be nice for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm supportive of length to be 1, and we can probably have a discussion about whether or not we want There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess I wanted @jswalden's opinion to be the one I presented ;) #argumentumadverecundiam |
||
|
||
<p> | ||
When the *Intl.NumberFormat.prototype.formatToParts* is called with an optional argument _value_, the following steps are taken: | ||
</p> | ||
|
||
<emu-alg> | ||
1. Let _nf_ be *this* value. | ||
1. If Type(_nf_) is not Object, throw a *TypeError* exception. | ||
1. If _nf_ does not have an [[initializedNumberFormat]] internal slot, throw a *TypeError* exception. | ||
1. If _value_ is not provided, let _value_ be *undefined*. | ||
1. Let _x_ be ? ToNumber(_value_). | ||
1. Return ? FormatNumberToParts(_nf_, _x_). | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think you can just directly return the result of FormatNumberToParts to ECMAScript. FormatNumberToParts returns a List of Records. You have to convert the List of Records into an Array of Objects. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @littledan - this is fixed now |
||
</emu-alg> | ||
</emu-clause> | ||
|
||
<emu-clause id="sec-intl.numberformat.prototype.resolvedoptions"> | ||
<h1>Intl.NumberFormat.prototype.resolvedOptions ()</h1> | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there is a missing code-path here... either an assertion that there is always a value for currencyDisplay, or an else with the right value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right, thanks. I think an assertion is fine, as we'd already throw a
RangeError
inInitializeNumberFormat
(§11.1.1 step 20):There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or did you mean that one of the valid values (code, symbol or name) should simply be in an
else
block without checking what it is?