generated from tc39/template-for-proposals
-
Notifications
You must be signed in to change notification settings - Fork 8
Expand file tree
/
Copy pathspec.emu
More file actions
346 lines (328 loc) · 21.3 KB
/
spec.emu
File metadata and controls
346 lines (328 loc) · 21.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
<!doctype html>
<meta charset="utf8">
<link rel="stylesheet" href="./spec.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/8.4/styles/github.min.css">
<script src="./spec.js"></script>
<pre class="metadata">
title: Amount
status: proposal
stage: 1
contributors: Ben Allen, Jesse Alama
location: https://github.com/tc39/proposal-amount/
</pre>
<emu-intro id="sec-decimal-intro-">
<h1>Introduction</h1>
<p>This specification consists of two parts:</p>
<ul>
<li>The specification of the <a href="https://github.com/tc39/proposal-amount/" title="Amount proposal (GitHub)">Amount proposal</a> and everything related to it, proposed to be added to ECMA​-262 in new sections;</li>
<li>A list of amendments to be made to ECMA-402.</li>
</ul>
<emu-note type="editor">
<p>
The changes proposed here are stacked on top of the <a href="https://tc39.es/proposal-intl-keep-trailing-zeros/">Keep Trailing Zeros</a> ECMA-402 proposal,
and include calls to ECMA-402 Abstract Operations from ECMA​-262 algorithms.
Where necessary, we intend to promote those semantics to ECMA​-262.
</p>
</emu-note>
</emu-intro>
<emu-clause id="sec-the-amount-object">
<h1>The Amount Object</h1>
<emu-intro id="sec-amount-intro">
<h1>Introduction</h1>
<p>An Amount is an object that wraps a numeric value—as a Number, BigInt, or String—together with an optional unit (e.g., mile, kilogram, EUR, JPY, USD-per-mile). One can intuitively understand an Amount as a value that, so to speak, knows what it is measuring.</p>
<p>When precision options (such as fractionDigits or significantDigits) are applied, or when unit conversion is performed, the numeric value is stored as a <dfn id="dfn-decimal-digit-string">decimal digit string</dfn>, which is a String in |StrDecimalLiteral| form or *"NaN"*. Otherwise, the original JavaScript value type (Number, BigInt, or String) is retained.</p>
<p>Rounding a mathematical value is an important part of this spec. When we say <dfn id="dfn-amount-rounding-mode">rounding mode</dfn> in this specification we simply refer to <emu-xref href="#table-intl-rounding-modes">ECMA-402's definition</emu-xref>.</p>
</emu-intro>
<emu-clause id="sec-amount-abstract-operations">
<h1>Abstract Operations</h1>
<!-- Copied and modified from ECMA-402 GetOption -->
<!-- The difference: -->
<!-- Added support for ~number~ as an expected data type -->
<emu-clause id="sec-getoption" type="abstract operation">
<h1>
GetOption (
_options_: an Object,
_property_: a property key,
_type_: ~boolean~, ~string~ or ~number~,
_values_: ~empty~ or a List of ECMAScript language values,
_default_: ~required~ or an ECMAScript language value,
): either a normal completion containing an ECMAScript language value or a throw completion
</h1>
<dl class="header">
<dt>description</dt>
<dd>It extracts the value of the specified property of _options_, converts it to the required _type_, checks whether it is allowed by _values_ if _values_ is not ~empty~, and substitutes _default_ if the value is *undefined*.</dd>
</dl>
<emu-alg>
1. Let _value_ be ? Get(_options_, _property_).
1. If _value_ is *undefined*, then
1. If _default_ is ~required~, throw a *RangeError* exception.
1. Return _default_.
1. If _type_ is ~boolean~, then
1. Set _value_ to ToBoolean(_value_).
1. Else if _type_ is ~number~, then
1. Set _value_ to ? ToNumber(_value_).
1. Else,
1. Assert: _type_ is ~string~.
1. Set _value_ to ? ToString(_value_).
1. If _values_ is not ~empty~ and _values_ does not contain _value_, throw a *RangeError* exception.
1. Return _value_.
</emu-alg>
</emu-clause>
<emu-clause id="sec-amount-getamountoptions" type="abstract operation">
<h1>GetAmountOptions (
_opts_: an Object
): either a normal completion containing a Record with fields [[FractionDigits]] (a non-negative integer or *undefined*), [[RoundingMode]] (a <emu-xref href="#dfn-amount-rounding-mode">rounding mode</emu-xref>), [[SignificantDigits]] (a positive integer or *undefined*), and [[Unit]] (a String or *undefined*) or a throw completion
</h1>
<dl class="header">
<dt>description</dt>
<dd>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*).</dd>
</dl>
<emu-alg>
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. Else if _significantDigits_ is not *undefined*, then
1. If _significantDigits_ is not an integral Number, throw a *RangeError* exception.
1. If _significantDigits_ < *1*<sub>𝔽</sub>, 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_ }.
</emu-alg>
</emu-clause>
<emu-clause id="sec-amount-getamountconverttooptions" type="abstract operation">
<h1>GetAmountConvertToOptions (
_opts_: an Object
): either a normal completion containing a Record with fields [[MinFractionDigits]] (a non-negative integer or *undefined*), [[MaxFractionDigits]] (a non-negative integer or *undefined*), [[RoundingMode]] (a <emu-xref href="#dfn-amount-rounding-mode">rounding mode</emu-xref>), [[RoundingPriority]] (a String), [[MinSignificantDigits]] (a positive integer or *undefined*), [[MaxSignificantDigits]] (a positive integer or *undefined*), [[Locale]] (a String or *undefined*), [[Usage]] (a String or *undefined*), and [[Unit]] (a String or *undefined*) or a throw completion
</h1>
<dl class="header">
<dt>description</dt>
<dd>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*).</dd>
</dl>
<emu-alg>
1. Let _opts_ be ? GetOptionsObject(_opts_).
1. Let _minFractionDigits_ be ? GetOption(_opts_, *"minimumfractionDigits"*, ~number~, ~empty~, *undefined*).
1. Let _maxFractionDigits_ be ? GetOption(_opts_, *"maximumfractionDigits"*, ~number~, ~empty~, *undefined*).
1. Let _roundingMode_ be ? GetOption(_opts_, *"roundingMode"*, ~string~, « *"ceil"*, *"floor"*, *"expand"*, *"trunc"*, *"halfCeil"*, *"halfFloor"*, *"halfExpand"*, *"halfTrunc"*, *"halfEven"* », *"halfEven"*).
1. Let _roundingPriority_ be ? GetOption(_opts_, *"roundingPriority"*, ~string~, ~empty~, *undefined*).
1. Let _minSignificantDigits_ be ? GetOption(_opts_, *"minimumSignificantDigits"*, ~number~, ~empty~, *undefined*).
1. Let _maxSignificantDigits_ be ? GetOption(_opts_, *"maximumSignificantDigits"*, ~number~, ~empty~, *undefined*).
1. Let _significantDigits_ be ? GetOption(_opts_, *"maximumSignificantDigits"*, ~number~, ~empty~, *undefined*).
1. Let _unit_ be ? GetOption(_opts_, *"unit"*, ~string~, ~empty~, *undefined*).
1. If _minFractionDigits_ is not *undefined* and not an integral Number, throw a *RangeError* exception.
1. If _maxFractionDigits_ is not *undefined* and not an integral Number, throw a *RangeError* exception.
1. If _minSignificantDigits_ is not *undefined* and not an integral Number, throw a *RangeError* exception.
1. If _maxSignificantDigits_ is not *undefined* and not an integral Number, throw a *RangeError* exception.
1. If _unit_ is the empty String, throw a *RangeError* exception.
1. Return the Record { [[MinimumFractionDigits]]: _minFractionDigits_, [[MaximumFractionDigits]]: _maxFractionDigits_, [[MinimumSignificantDigits]]: _minSignificantDigits_, [[MaximumSignificantDigits]]: _maxSignificantDigits_, [[RoundingMode]]: _roundingMode_, [[RoundingPriority]]: _roundingPriority_, [[SignificantDigits]]: _significantDigits_, [[Unit]]: _unit_ }.
</emu-alg>
<emu-note type="editor">
<p>
This abstract operation will need to be overridden in the 402 part because it will read additional Intl-specific properties beyond these.
</p>
</emu-note>
</emu-clause>
<emu-clause id="sec-amount-getunitconversionfactor" type="implementation-defined abstract operation">
<h1>GetUnitConversionFactor (
_unit_: a String
): either a normal completion containing a Record with fields [[Category]] (a String), [[Factor]] (a Number), and [[Offset]] (a Number) or a throw completion
</h1>
<dl class="header">
<dt>description</dt>
<dd>It returns the conversion data for converting _unit_ to its base unit within its unit category.</dd>
</dl>
<emu-alg>
1. If _unit_ does not have a corresponding <convertUnit> element in <a href="https://unicode.org/reports/tr35/tr35-info.html#Unit_Conversion">UTS #35 Part 6 Supplemental, Unit Conversion</a>, or if that element specifies a <code>special</code> conversion, throw a *RangeError* exception.
1. Let _category_ be the unit category of _unit_ as specified by the CLDR unit conversion data.
1. Let _factor_ be the Number value closest to the rational conversion factor for _unit_ as specified by the CLDR unit conversion data.
1. If the CLDR unit conversion data specifies a conversion offset for _unit_, let _offset_ be the Number value closest to that rational offset; otherwise, let _offset_ be *+0*<sub>𝔽</sub>.
1. Return the Record { [[Category]]: _category_, [[Factor]]: _factor_, [[Offset]]: _offset_ }.
</emu-alg>
<emu-note>
<p>The formula for converting a value in _unit_ to its base unit is: <i>baseValue</i> = <i>value</i> × [[Factor]] + [[Offset]].</p>
<p>CLDR expresses conversion factors as rational numbers (e.g., 0.3048/12 for inch-to-meter). These rational values are converted to Numbers before use, so conversion arithmetic is subject to the precision of IEEE 754 binary64. For example, converting 1.75 feet to inches yields 1.75 × 0.3048 / (0.3048 / 12) = 20.999999999999996, not exactly 21.</p>
<p>CLDR also defines nonlinear conversions via the <code>special</code> attribute (e.g., the Beaufort scale). These cannot be expressed as a linear factor and offset, so they are excluded from this operation.</p>
</emu-note>
</emu-clause>
<emu-clause id="sec-amount-convertunitvalue" type="abstract operation">
<h1>ConvertUnitValue (
_value_: a Number,
_sourceUnit_: a String,
_targetUnit_: a String
): either a normal completion containing a Number or a throw completion
</h1>
<dl class="header">
<dt>description</dt>
<dd>It converts _value_ from _sourceUnit_ to _targetUnit_ using Number arithmetic.</dd>
</dl>
<emu-alg>
1. Let _sourceConv_ be ? GetUnitConversionFactor(_sourceUnit_).
1. Let _targetConv_ be ? GetUnitConversionFactor(_targetUnit_).
1. If SameValue(_sourceConv_.[[Category]], _targetConv_.[[Category]]) is *false*, throw a *RangeError* exception.
1. Let _sourceFactor_ be _sourceConv_.[[Factor]].
1. Let _sourceOffset_ be _sourceConv_.[[Offset]].
1. Let _targetFactor_ be _targetConv_.[[Factor]].
1. Let _targetOffset_ be _targetConv_.[[Offset]].
1. Let _baseValue_ be _value_ × _sourceFactor_ + _sourceOffset_.
1. Let _result_ be (_baseValue_ - _targetOffset_) / _targetFactor_.
1. Return _result_.
</emu-alg>
</emu-clause>
</emu-clause>
<emu-clause id="sec-the-amount-constructor">
<h1>The Amount Constructor</h1>
<p>The Amount constructor:</p>
<ul>
<li>is <dfn>%Amount%</dfn>.</li>
<li>is the initial value of the the *"Amount"* property of the global object.</li>
<li>creates and initializes a new Amount object when called as a constructor</li>
<li>may be used as the value of an *extends* clause of a class definition.</li>
</ul>
<emu-clause id="sec-the-amount-constructor-value">
<h1>Amount ( _x_ [ , _opts_ ] )</h1>
<emu-alg>
1. If NewTarget is *undefined*, throw a *TypeError* exception.
1. If _x_ is not a Number, not a BigInt, and not a String, throw a *TypeError* exception.
1. If _x_ is a String, then
1. Let _text_ be StringToCodePoints(_x_).
1. Let _parsed_ be ParseText(_text_, |StringNumericLiteral|).
1. If _parsed_ is a List of errors, throw a *RangeError* exception.
1. Let _validatedOpts_ be ? GetAmountOptions(_opts_).
1. Let _roundingMode_ be _validatedOpts_.[[RoundingMode]].
1. Let _fractionDigits_ be _validatedOpts_.[[FractionDigits]].
1. Let _significantDigits_ be _validatedOpts_.[[SignificantDigits]].
1. Let _unit_ be _validatedOpts_.[[Unit]].
1. If _x_ is a Number or _x_ is a BigInt, then
1. Let _value_ be _x_.
1. Else,
1. Assert: _x_ is a String.
1. TODO Let _intlObject_ be an Object suitable as a first argument <emu-xref href="#sec-formatnumerictostring">FormatNumericToString</emu-xref>, using _fractionDigits_, _significantDigits_, and _roundingMode_.
1. Let _intlMV_ be ! ToIntlMathematicalValue(_x_).
1. Let _formatted_ be FormatNumericToString(_intlObject_, _intlMV_.[[Value]], _intlMV_.[[StringDigitCount]]).
1. Let _value_ be _formatted_.[[FormattedString]].
1. Let _O_ be OrdinaryObjectCreate(%Amount.prototype%, « [[AmountValue]], [[Unit]] »).
1. Set _O_.[[AmountValue]] to _value_.
1. Set _O_.[[Unit]] to _unit_.
1. Return _O_.
</emu-alg>
<emu-note>
<p>When no precision options are given, Number and BigInt arguments are stored directly in [[AmountValue]], preserving the original type. String arguments are normalized to StrDecimalLiteral form. When precision options are specified, [[AmountValue]] always holds a String.</p>
</emu-note>
<emu-note type="editor">
<p>
We intend to move 402's FormatNumericToString, and its dependent AOs, to 262, possibly renamed.
</p>
</emu-note>
</emu-clause>
</emu-clause>
</emu-clause>
<emu-clause id="sec-amount-prototype-properties">
<h1>Properties of the Amount Prototype</h1>
<emu-clause id="sec-amount.prototype.value">
<h1>get Amount.prototype.value</h1>
<p>This accessor property, whose set accessor function is *undefined*, returns the numeric value of the Amount. Its get accessor function performs the following steps when called:</p>
<emu-alg>
1. Let _O_ be the *this* value.
1. Perform ? RequireInternalSlot(_O_, [[AmountValue]]).
1. Return _O_.[[AmountValue]].
</emu-alg>
<emu-note>
<p>The value may be a Number, BigInt, or String. It is a String when precision options were applied during construction or when the Amount is the result of unit conversion.</p>
</emu-note>
</emu-clause>
<emu-clause id="sec-amount.prototype.unit">
<h1>get Amount.prototype.unit</h1>
<p>This accessor property, whose set accessor function is *undefined*, returns a String value (or *undefined*) indicating the unit that this Amount has. Its get accessor function performs the following steps when called:</p>
<emu-alg>
1. Let _O_ be the *this* value.
1. Perform ? RequireInternalSlot(_O_, [[AmountValue]]).
1. Return _O_.[[Unit]].
</emu-alg>
</emu-clause>
<emu-clause id="sec-amount.prototype.tostring">
<h1>Amount.prototype.toString ( )</h1>
<p>This method returns a String representation of the Amount, including a unit indicator in bracket notation.</p>
<p>It performs the following steps when called:</p>
<emu-alg>
1. Let _O_ be the *this* value.
1. Perform ? RequireInternalSlot(_O_, [[AmountValue]]).
1. Let _v_ be _O_.[[AmountValue]].
1. Let _u_ be _O_.[[Unit]].
1. If _v_ is a String, then
1. Let _valueStr_ be _v_.
1. Else if _v_ is a Number, then
1. Let _valueStr_ be Number::toString(_v_, 10).
1. Else,
1. Assert: _v_ is a BigInt.
1. Let _valueStr_ be BigInt::toString(_v_, 10).
1. If _u_ is *undefined*, return the string-concatenation of _valueStr_ and *"[]"*.
1. Return the string-concatenation of _valueStr_, *"["*, _u_, and *"]"*.
</emu-alg>
</emu-clause>
<emu-clause id="sec-amount.prototype.tolocalestring">
<h1>Amount.prototype.toLocaleString ( [ _reserved1_ [ , _reserved2_ ] ] )</h1>
<p>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 the following specification of this method is used:</p>
<p>It performs the following steps when called:</p>
<emu-alg>
1. Let _O_ be the *this* value.
1. Perform ? RequireInternalSlot(_O_, [[AmountValue]]).
1. Return ? Call(%Amount.prototype.toString%, _O_, « »).
</emu-alg>
<p>The meanings of the optional parameters to this method are defined in the ECMA-402 specification; implementations that do not include ECMA-402 support must not use those parameter positions for anything else.</p>
</emu-clause>
<emu-clause id="sec-amount.prototype.convertto">
<h1>Amount.prototype.convertTo ( _options_ )</h1>
<p>This method returns a new Amount whose value is the result of converting this Amount’s value from its current unit to a target unit. The target unit is specified by _options_.</p>
<p>It performs the following steps when called:</p>
<emu-alg>
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 _roundingPriority_ be _validatedOpts_.[[RoundingPriority]].
1. Let _minFractionDigits_ be _validatedOpts_.[[MinimumFractionDigits]].
1. Let _maxFractionDigits_ be _validatedOpts_.[[MaximumFractionDigits]].
1. Let _minSignificantDigits_ be _validatedOpts_.[[MinimumSignificantDigits]].
1. Let _maxSignificantDigits_ be _validatedOpts_.[[MaximumSignificantDigits]].
1. Let _v_ be _O_.[[AmountValue]].
1. If _v_ is a Number, then
1. Let _sourceValue_ be _v_.
1. Else if _v_ is a BigInt, then
1. Let _sourceValue_ be 𝔽(ℝ(_v_)).
1. Else,
1. Assert: _v_ is a String.
1. Let _sourceValue_ be StringToNumber(_v_).
1. Let _convertedValue_ be ? ConvertUnitValue(_sourceValue_, _sourceUnit_, _targetUnit_).
1. TODO Let _intlObject_ be an Object suitable as the first argument of <emu-xref href="#sec-formatnumerictostring">FormatNumericToString</emu-xref>, using _minFractionDigits_, _maxFractionDigits_, _minSignificantDigits_, _maxSignificantDigits_, _roundingPriority_, and _roundingMode_.
1. Let _formatted_ be FormatNumericToString(_intlObject_, _convertedValue_, 0).
1. Let _result_ be OrdinaryObjectCreate(%Amount.prototype%, « [[AmountValue]], [[Unit]] »).
1. Set _result_.[[AmountValue]] to _formatted_.[[FormattedString]].
1. Set _result_.[[Unit]] to _targetUnit_.
1. Return _result_.
</emu-alg>
<emu-note>
<p>An ECMAScript implementation that includes the ECMA-402 Internationalization API supersedes this method to additionally support locale-based and usage-based unit conversion via CLDR unit preferences data. Without ECMA-402, only explicit unit-to-unit conversion (via the *"unit"* option) is supported.</p>
</emu-note>
</emu-clause>
</emu-clause>
<emu-import href="./intl.emu"></emu-import>
<emu-clause id="sec-normative-references">
<h1>Normative References</h1>
<ul>
<li>
<a href="https://unicode.org/reports/tr35/">Unicode Technical Standard #35: Unicode Locale Data Markup Language (LDML)</a>
<ul>
<li>
<a href="https://unicode.org/reports/tr35/tr35-info.html#Unit_Conversion">Part 6 Supplemental, Unit Conversion</a>
</li>
</ul>
</li>
</ul>
</emu-clause>