ranger-compiler 3.1.1 → 3.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2527 -0
- package/LICENSE +28 -0
- package/LICENSE-MIT +21 -0
- package/README.md +1650 -1875
- package/dist/Lang.rgr +10946 -5662
- package/dist/README.md +3 -2
- package/dist/api.d.ts +2023 -61
- package/dist/api.js +68930 -27355
- package/dist/lib/ACEEditor.rgr +2 -0
- package/dist/lib/Ajax.rgr +2 -0
- package/dist/lib/CmdParams.rgr +2 -0
- package/dist/lib/Crypto.rgr +2 -0
- package/dist/lib/DOMLib.rgr +2 -0
- package/dist/lib/Engine3D.rgr +2 -0
- package/dist/lib/ImmutableVector.rgr +2 -0
- package/dist/lib/IndexedDB.rgr +2 -0
- package/dist/lib/IsoDate/DateMath.rgr +2 -0
- package/dist/lib/IsoDate/IsoCalendar.rgr +2 -0
- package/dist/lib/IsoDate/IsoDateParse.rgr +2 -0
- package/dist/lib/IsoDateLib.rgr +2 -0
- package/dist/lib/JSON.rgr +4906 -48
- package/dist/lib/JinxProcess.rgr +2 -0
- package/dist/lib/RangerProcess.rgr +2 -0
- package/dist/lib/Regex/RegexMatch.rgr +2 -0
- package/dist/lib/RegexLib.rgr +2 -0
- package/dist/lib/SQL.rgr +2 -0
- package/dist/lib/ServiceLib.rgr +2 -0
- package/dist/lib/Shell.rgr +326 -0
- package/dist/lib/Storage.rgr +2 -0
- package/dist/lib/Time.rgr +2 -0
- package/dist/lib/Timers.rgr +2 -0
- package/dist/lib/TypedArrays.rgr +2 -0
- package/dist/lib/ViewLib.rgr +2 -0
- package/dist/lib/WebLib.rgr +2 -0
- package/dist/lib/WebServerLib.rgr +2 -0
- package/dist/lib/apple/AppleAppBuilder.rgr +572 -0
- package/dist/lib/apple/AppleAppSpec.rgr +201 -0
- package/dist/lib/apple/AppleDevice.rgr +295 -0
- package/dist/lib/apple/AppleDeviceDoctor.rgr +453 -0
- package/dist/lib/apple/AppleSigning.rgr +379 -0
- package/dist/lib/apple/AppleSimulator.rgr +248 -0
- package/dist/lib/apple/AppleTarget.rgr +185 -0
- package/dist/lib/apple/AppleToolchain.rgr +458 -0
- package/dist/lib/apple/README.md +311 -0
- package/dist/lib/apple/apple_test.rgr +669 -0
- package/dist/lib/core/README.md +251 -0
- package/dist/lib/core/RgBase.rgr +313 -0
- package/dist/lib/core/RgNum.rgr +653 -0
- package/dist/lib/core/RgText.rgr +680 -0
- package/dist/lib/core/RgU32.rgr +309 -0
- package/dist/lib/ranger-dir.rgr +27 -5
- package/dist/lib/shell_test.rgr +193 -0
- package/dist/lib/stdlib.rgr +1176 -665
- package/dist/lib/stdops.rgr +2 -0
- package/dist/package.json +1 -1
- package/dist/rgrc.js +73013 -37819
- package/dist/stdops.rgr +2 -0
- package/package.json +1121 -205
|
@@ -0,0 +1,653 @@
|
|
|
1
|
+
; SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
; =============================================================================
|
|
4
|
+
; RgNum — IEEE-754 double primitives, in pure Ranger
|
|
5
|
+
; =============================================================================
|
|
6
|
+
; The bottom of lib/core/. Everything numeric in the core library stands on this
|
|
7
|
+
; file, and it has no imports of its own.
|
|
8
|
+
;
|
|
9
|
+
; It exists because Ranger's operator set is thinner than it looks. `sqrt`,
|
|
10
|
+
; `sin`, `cos`, `tan`, `asin`, `acos`, `atan2` and `fabs` are host operators with
|
|
11
|
+
; per-target templates. There is NO `exp`, NO `log`, NO `pow`, NO `round`, NO
|
|
12
|
+
; `atan` and no double-typed `min`/`max`. And `floor`/`ceil` are declared
|
|
13
|
+
; `_:int (value:double)` — they return an int, which is a different function from
|
|
14
|
+
; the one IEEE arithmetic needs.
|
|
15
|
+
;
|
|
16
|
+
; So `exp`, `log` and `pow` below are not conveniences. Without them the target
|
|
17
|
+
; set has no portable exponential, logarithm or power at all.
|
|
18
|
+
;
|
|
19
|
+
; NAMING. A static method cannot take the name of a global operator: the
|
|
20
|
+
; resolver rewrites `RgNum.floor(x)` into the operator call and then reports
|
|
21
|
+
; "Class RgNum does not have method floor". Nine names are affected — floor,
|
|
22
|
+
; ceil, sqrt, sin, cos, tan, asin, acos, atan2 — and each carries a `D` suffix
|
|
23
|
+
; here, the convention DateTime.rgr already uses for `floorD` / `truncD` /
|
|
24
|
+
; `modD`. The suffix reads as "the double-domain one", which for floor and ceil
|
|
25
|
+
; is a real distinction: the OPERATORS return `int`.
|
|
26
|
+
;
|
|
27
|
+
; Two per-target hazards are handled here rather than left to callers, because
|
|
28
|
+
; both were found the hard way in the JS engine this code is lifted from
|
|
29
|
+
; (gallery/game_engine/v2/interp/migrate/src/ComponentEngine.rgr):
|
|
30
|
+
;
|
|
31
|
+
; 1. `to_int` rides the target's int width, and Ranger's int is 32 BITS on the
|
|
32
|
+
; C++ target. `to_double (to_int v)` therefore saturates to INT32_MIN for
|
|
33
|
+
; any |v| >= 2^31 there. `floorD` below stays in the double domain by adding
|
|
34
|
+
; and subtracting 2^52, so it is exact across the whole double range.
|
|
35
|
+
;
|
|
36
|
+
; 2. Negative zero has no literal. Writing `0.0 - v` yields +0 for BOTH signs,
|
|
37
|
+
; which silently collapsed the distinction in five separate places. It has
|
|
38
|
+
; to be built by MULTIPLYING a double-typed variable by -1: the C++ writer
|
|
39
|
+
; prints double literals bare, so `0.0 * (0.0 - 1.0)` was constant-folded as
|
|
40
|
+
; integer 0 * -1 = +0 there. `negZero()` does it through a binding.
|
|
41
|
+
;
|
|
42
|
+
; Every function here is total: NaN and the infinities are values, never errors.
|
|
43
|
+
; =============================================================================
|
|
44
|
+
|
|
45
|
+
class RgNum {
|
|
46
|
+
|
|
47
|
+
; ---- the three values with no literal form ------------------------------
|
|
48
|
+
|
|
49
|
+
static sfn nan:double () {
|
|
50
|
+
return (0.0 / 0.0)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
static sfn inf:double () {
|
|
54
|
+
return (1.0 / 0.0)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
static sfn negInf:double () {
|
|
58
|
+
def one:double 1.0
|
|
59
|
+
return (0.0 - (one / 0.0))
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
; Negative zero, as -1 divided by infinity.
|
|
63
|
+
;
|
|
64
|
+
; This is the third attempt and the reason is measured. `0.0 * (0.0 - 1.0)`
|
|
65
|
+
; gives +0 on C++, because that writer prints double literals bare and the
|
|
66
|
+
; product is constant-folded as integer `0 * -1`. Binding it first —
|
|
67
|
+
; `def z:double 0.0` then `z * (0.0 - 1.0)` — fixes C++ but NOT Python: that
|
|
68
|
+
; writer emits the binding itself as `z = 0`, an int literal, so the sign is
|
|
69
|
+
; gone one line earlier. Verified by tests/core-targets.test.ts, which had
|
|
70
|
+
; Python answering `negZero_is_neg=false` while es6, Go and C++ said true.
|
|
71
|
+
;
|
|
72
|
+
; A DIVISION cannot be folded into an int on any of them: every target routes
|
|
73
|
+
; double division through its own helper (Python's raises ZeroDivisionError
|
|
74
|
+
; otherwise, so `r_div_f64` is already there), and -1/Inf is -0 in IEEE-754.
|
|
75
|
+
static sfn negZero:double () {
|
|
76
|
+
def negOne:double (0.0 - 1.0)
|
|
77
|
+
return (negOne / (RgNum.inf()))
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
; ---- predicates --------------------------------------------------------
|
|
81
|
+
|
|
82
|
+
static sfn isNaN:boolean (v:double) {
|
|
83
|
+
return (v != v)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
static sfn isFinite:boolean (v:double) {
|
|
87
|
+
if (v != v) {
|
|
88
|
+
return false
|
|
89
|
+
}
|
|
90
|
+
def i:double (RgNum.inf())
|
|
91
|
+
if (v == i) {
|
|
92
|
+
return false
|
|
93
|
+
}
|
|
94
|
+
if (v == (0.0 - i)) {
|
|
95
|
+
return false
|
|
96
|
+
}
|
|
97
|
+
return true
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
; The only way to tell -0 from +0 is the sign of its reciprocal.
|
|
101
|
+
static sfn isNegZero:boolean (v:double) {
|
|
102
|
+
if (v != 0.0) {
|
|
103
|
+
return false
|
|
104
|
+
}
|
|
105
|
+
return ((1.0 / v) < 0.0)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
; True for -0 as well as for anything strictly below zero. This is the
|
|
109
|
+
; question every sign-preserving rounding rule actually asks.
|
|
110
|
+
static sfn isNegative:boolean (v:double) {
|
|
111
|
+
if (v < 0.0) {
|
|
112
|
+
return true
|
|
113
|
+
}
|
|
114
|
+
return (RgNum.isNegZero(v))
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
static sfn isInteger:boolean (v:double) {
|
|
118
|
+
if (false == (RgNum.isFinite(v))) {
|
|
119
|
+
return false
|
|
120
|
+
}
|
|
121
|
+
return ((RgNum.trunc(v)) == v)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
; ---- magnitude ---------------------------------------------------------
|
|
125
|
+
|
|
126
|
+
; abs(-0) is +0, which `fabs` gives on every target but which is worth
|
|
127
|
+
; pinning here so callers do not have to know that.
|
|
128
|
+
static sfn abs:double (v:double) {
|
|
129
|
+
if (v != v) {
|
|
130
|
+
return v
|
|
131
|
+
}
|
|
132
|
+
if (RgNum.isNegZero(v)) {
|
|
133
|
+
return 0.0
|
|
134
|
+
}
|
|
135
|
+
if (v < 0.0) {
|
|
136
|
+
return (0.0 - v)
|
|
137
|
+
}
|
|
138
|
+
return v
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
; ---- rounding, all four in the double domain ---------------------------
|
|
142
|
+
|
|
143
|
+
; 2^52: at or above this magnitude a double is already integral, and below
|
|
144
|
+
; it the add/subtract round-trip lands exactly on an integer.
|
|
145
|
+
static sfn roundingBig:double () {
|
|
146
|
+
return 4503599627370496.0
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
; Floor toward NEGATIVE infinity. floor(-0) is -0.
|
|
150
|
+
static sfn floorD:double (v:double) {
|
|
151
|
+
if (v != v) {
|
|
152
|
+
return v
|
|
153
|
+
}
|
|
154
|
+
if (false == (RgNum.isFinite(v))) {
|
|
155
|
+
return v
|
|
156
|
+
}
|
|
157
|
+
; +0 and -0 both floor to THEMSELVES; the arithmetic below would turn
|
|
158
|
+
; -0 into +0, and ceil's sign dance reads floor(-0) back as -0.
|
|
159
|
+
if (v == 0.0) {
|
|
160
|
+
return v
|
|
161
|
+
}
|
|
162
|
+
def big:double (RgNum.roundingBig())
|
|
163
|
+
def av:double v
|
|
164
|
+
if (av < 0.0) {
|
|
165
|
+
av = (0.0 - av)
|
|
166
|
+
}
|
|
167
|
+
if (av >= big) {
|
|
168
|
+
return v
|
|
169
|
+
}
|
|
170
|
+
def t:double 0.0
|
|
171
|
+
if (v >= 0.0) {
|
|
172
|
+
t = ((v + big) - big)
|
|
173
|
+
} {
|
|
174
|
+
t = ((v - big) + big)
|
|
175
|
+
}
|
|
176
|
+
if (t > v) {
|
|
177
|
+
return (t - 1.0)
|
|
178
|
+
}
|
|
179
|
+
return t
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
; Ceiling toward POSITIVE infinity. Every x in (-1, 0) ceils to NEGATIVE
|
|
183
|
+
; zero, and so does -0 itself — that is the whole reason this is not just
|
|
184
|
+
; `-floor(-x)`.
|
|
185
|
+
static sfn ceilD:double (v:double) {
|
|
186
|
+
if (v != v) {
|
|
187
|
+
return v
|
|
188
|
+
}
|
|
189
|
+
if (false == (RgNum.isFinite(v))) {
|
|
190
|
+
return v
|
|
191
|
+
}
|
|
192
|
+
def r:double (0.0 - (RgNum.floorD((0.0 - v))))
|
|
193
|
+
if (r == 0.0) {
|
|
194
|
+
if (RgNum.isNegative(v)) {
|
|
195
|
+
return (RgNum.negZero())
|
|
196
|
+
}
|
|
197
|
+
return 0.0
|
|
198
|
+
}
|
|
199
|
+
return r
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
; Truncation toward zero. Every x in (-1, 0) truncates to NEGATIVE zero.
|
|
203
|
+
static sfn trunc:double (v:double) {
|
|
204
|
+
if (v != v) {
|
|
205
|
+
return v
|
|
206
|
+
}
|
|
207
|
+
if (false == (RgNum.isFinite(v))) {
|
|
208
|
+
return v
|
|
209
|
+
}
|
|
210
|
+
if (v == 0.0) {
|
|
211
|
+
return v
|
|
212
|
+
}
|
|
213
|
+
if (v < 0.0) {
|
|
214
|
+
def t:double (0.0 - (RgNum.floorD((0.0 - v))))
|
|
215
|
+
if (t == 0.0) {
|
|
216
|
+
return (RgNum.negZero())
|
|
217
|
+
}
|
|
218
|
+
return t
|
|
219
|
+
}
|
|
220
|
+
return (RgNum.floorD(v))
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
; Round half UP — toward +Infinity, not away from zero. round(-0.5) is -0
|
|
224
|
+
; and round(0.5) is 1, so the two are not mirror images.
|
|
225
|
+
static sfn round:double (v:double) {
|
|
226
|
+
if (v != v) {
|
|
227
|
+
return v
|
|
228
|
+
}
|
|
229
|
+
if (false == (RgNum.isFinite(v))) {
|
|
230
|
+
return v
|
|
231
|
+
}
|
|
232
|
+
if (v == 0.0) {
|
|
233
|
+
return v
|
|
234
|
+
}
|
|
235
|
+
def big:double (RgNum.roundingBig())
|
|
236
|
+
def av:double (RgNum.abs(v))
|
|
237
|
+
if (av >= big) {
|
|
238
|
+
return v
|
|
239
|
+
}
|
|
240
|
+
def r:double (RgNum.floorD((v + 0.5)))
|
|
241
|
+
; Everything in [-0.5, 0) rounds to negative zero.
|
|
242
|
+
if (r == 0.0) {
|
|
243
|
+
if (v < 0.0) {
|
|
244
|
+
return (RgNum.negZero())
|
|
245
|
+
}
|
|
246
|
+
return 0.0
|
|
247
|
+
}
|
|
248
|
+
return r
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
; -0 for -0, +0 for +0, NaN for NaN, and ±1 otherwise.
|
|
252
|
+
static sfn sign:double (v:double) {
|
|
253
|
+
if (v != v) {
|
|
254
|
+
return v
|
|
255
|
+
}
|
|
256
|
+
if (v == 0.0) {
|
|
257
|
+
return v
|
|
258
|
+
}
|
|
259
|
+
if (v < 0.0) {
|
|
260
|
+
return (0.0 - 1.0)
|
|
261
|
+
}
|
|
262
|
+
return 1.0
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
; ---- ordering ----------------------------------------------------------
|
|
266
|
+
; No double-typed `min`/`max` operator exists. NaN poisons both, and the
|
|
267
|
+
; signed zeroes are ordered: max(-0, +0) is +0, min(-0, +0) is -0.
|
|
268
|
+
|
|
269
|
+
static sfn min2:double (a:double b:double) {
|
|
270
|
+
if (a != a) {
|
|
271
|
+
return a
|
|
272
|
+
}
|
|
273
|
+
if (b != b) {
|
|
274
|
+
return b
|
|
275
|
+
}
|
|
276
|
+
if (a < b) {
|
|
277
|
+
return a
|
|
278
|
+
}
|
|
279
|
+
if (b < a) {
|
|
280
|
+
return b
|
|
281
|
+
}
|
|
282
|
+
; Equal, so the only remaining distinction is the sign of a zero.
|
|
283
|
+
if (RgNum.isNegZero(a)) {
|
|
284
|
+
return a
|
|
285
|
+
}
|
|
286
|
+
if (RgNum.isNegZero(b)) {
|
|
287
|
+
return b
|
|
288
|
+
}
|
|
289
|
+
return a
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
static sfn max2:double (a:double b:double) {
|
|
293
|
+
if (a != a) {
|
|
294
|
+
return a
|
|
295
|
+
}
|
|
296
|
+
if (b != b) {
|
|
297
|
+
return b
|
|
298
|
+
}
|
|
299
|
+
if (a > b) {
|
|
300
|
+
return a
|
|
301
|
+
}
|
|
302
|
+
if (b > a) {
|
|
303
|
+
return b
|
|
304
|
+
}
|
|
305
|
+
if (RgNum.isNegZero(a)) {
|
|
306
|
+
return b
|
|
307
|
+
}
|
|
308
|
+
return a
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
; ---- exponential and logarithm -----------------------------------------
|
|
312
|
+
; There is no `exp` and no `log` operator on any target, so both are series
|
|
313
|
+
; expansions after range reduction. Accurate to the last few bits of a
|
|
314
|
+
; double across the usable range, which is what the JS engine's conformance
|
|
315
|
+
; suite measures them against.
|
|
316
|
+
|
|
317
|
+
static sfn ln2:double () {
|
|
318
|
+
return 0.6931471805599453
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
static sfn ln10:double () {
|
|
322
|
+
return 2.302585092994046
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
; e^x, by writing x as k*ln2 + r with |r| <= ln2/2 and running the Taylor
|
|
326
|
+
; series on r, which converges in under 24 terms at that magnitude.
|
|
327
|
+
static sfn exp:double (x:double) {
|
|
328
|
+
if (x != x) {
|
|
329
|
+
return x
|
|
330
|
+
}
|
|
331
|
+
def i:double (RgNum.inf())
|
|
332
|
+
if (x == i) {
|
|
333
|
+
return i
|
|
334
|
+
}
|
|
335
|
+
if (x == (0.0 - i)) {
|
|
336
|
+
return 0.0
|
|
337
|
+
}
|
|
338
|
+
def l2:double (RgNum.ln2())
|
|
339
|
+
def kD:double (RgNum.floorD(((x / l2) + 0.5)))
|
|
340
|
+
def r:double (x - (kD * l2))
|
|
341
|
+
def term:double 1.0
|
|
342
|
+
def sum:double 1.0
|
|
343
|
+
def n:int 1
|
|
344
|
+
while (n < 24) {
|
|
345
|
+
term = ((term * r) / (to_double n))
|
|
346
|
+
sum = (sum + term)
|
|
347
|
+
n = (n + 1)
|
|
348
|
+
}
|
|
349
|
+
def k:int (to_int kD)
|
|
350
|
+
def scale:double 1.0
|
|
351
|
+
if (k >= 0) {
|
|
352
|
+
scale = (RgNum.pow2(k))
|
|
353
|
+
} {
|
|
354
|
+
def down:double (RgNum.pow2((0 - k)))
|
|
355
|
+
scale = (1.0 / down)
|
|
356
|
+
}
|
|
357
|
+
return (sum * scale)
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
; Natural logarithm. x = m * 2^k with m in [1, 2), then the atanh series on
|
|
361
|
+
; (m-1)/(m+1), which converges far faster near the ends of the range than
|
|
362
|
+
; log(1+u) does.
|
|
363
|
+
static sfn log:double (x:double) {
|
|
364
|
+
if (x != x) {
|
|
365
|
+
return x
|
|
366
|
+
}
|
|
367
|
+
def i:double (RgNum.inf())
|
|
368
|
+
if (x < 0.0) {
|
|
369
|
+
return (RgNum.nan())
|
|
370
|
+
}
|
|
371
|
+
if (x == 0.0) {
|
|
372
|
+
return (0.0 - i)
|
|
373
|
+
}
|
|
374
|
+
if (x == i) {
|
|
375
|
+
return i
|
|
376
|
+
}
|
|
377
|
+
def k:int 0
|
|
378
|
+
def m:double x
|
|
379
|
+
while (m >= 2.0) {
|
|
380
|
+
m = (m / 2.0)
|
|
381
|
+
k = (k + 1)
|
|
382
|
+
}
|
|
383
|
+
while (m < 1.0) {
|
|
384
|
+
m = (m * 2.0)
|
|
385
|
+
k = (k - 1)
|
|
386
|
+
}
|
|
387
|
+
def z:double ((m - 1.0) / (m + 1.0))
|
|
388
|
+
def z2:double (z * z)
|
|
389
|
+
def term:double z
|
|
390
|
+
def sum:double z
|
|
391
|
+
def n:int 1
|
|
392
|
+
while (n < 30) {
|
|
393
|
+
term = (term * z2)
|
|
394
|
+
sum = (sum + (term / (to_double ((2 * n) + 1))))
|
|
395
|
+
n = (n + 1)
|
|
396
|
+
}
|
|
397
|
+
def l2:double (RgNum.ln2())
|
|
398
|
+
return ((2.0 * sum) + ((to_double k) * l2))
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
; ---- powers ------------------------------------------------------------
|
|
402
|
+
|
|
403
|
+
; 2^n for a non-negative int n, by squaring. Used by exp's scaling step, so
|
|
404
|
+
; it must not go through the general pow.
|
|
405
|
+
static sfn pow2:double (n:int) {
|
|
406
|
+
def acc:double 1.0
|
|
407
|
+
def base:double 2.0
|
|
408
|
+
def e:int n
|
|
409
|
+
if (e < 0) {
|
|
410
|
+
e = 0
|
|
411
|
+
}
|
|
412
|
+
while (e > 0) {
|
|
413
|
+
if ((bit_and e 1) == 1) {
|
|
414
|
+
acc = (acc * base)
|
|
415
|
+
}
|
|
416
|
+
base = (base * base)
|
|
417
|
+
e = (bit_shr e 1)
|
|
418
|
+
}
|
|
419
|
+
return acc
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
; An INTEGER power, by squaring. Exact for small exponents, which is what
|
|
423
|
+
; the formatting code wants when it scales by a power of ten.
|
|
424
|
+
static sfn powInt:double (base:double expn:int) {
|
|
425
|
+
def acc:double 1.0
|
|
426
|
+
def b:double base
|
|
427
|
+
def e:int expn
|
|
428
|
+
def invert:boolean false
|
|
429
|
+
if (e < 0) {
|
|
430
|
+
invert = true
|
|
431
|
+
e = (0 - e)
|
|
432
|
+
}
|
|
433
|
+
while (e > 0) {
|
|
434
|
+
if ((bit_and e 1) == 1) {
|
|
435
|
+
acc = (acc * b)
|
|
436
|
+
}
|
|
437
|
+
b = (b * b)
|
|
438
|
+
e = (bit_shr e 1)
|
|
439
|
+
}
|
|
440
|
+
if invert {
|
|
441
|
+
if (acc != 0.0) {
|
|
442
|
+
return (1.0 / acc)
|
|
443
|
+
}
|
|
444
|
+
return (RgNum.inf())
|
|
445
|
+
}
|
|
446
|
+
return acc
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
; 10^n, the one power the number formatters ask for by name.
|
|
450
|
+
static sfn pow10:double (n:int) {
|
|
451
|
+
return (RgNum.powInt(10.0 n))
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
; ECMA-262 Number::exponentiate. The exponent is a DOUBLE: coercing it to an
|
|
455
|
+
; int first truncates, so pow(4, 0.5) answered 1 instead of 2.
|
|
456
|
+
;
|
|
457
|
+
; The special cases are not decoration — the spec fixes an answer for each of
|
|
458
|
+
; them, and several disagree with what the identity exp(e * log(b)) gives.
|
|
459
|
+
static sfn pow:double (base:double expo:double) {
|
|
460
|
+
def i:double (RgNum.inf())
|
|
461
|
+
; A NaN exponent poisons the result, but an exponent of ZERO answers 1
|
|
462
|
+
; even for a NaN base. Both rules come before any test on the base.
|
|
463
|
+
if (expo != expo) {
|
|
464
|
+
return (RgNum.nan())
|
|
465
|
+
}
|
|
466
|
+
if (expo == 0.0) {
|
|
467
|
+
return 1.0
|
|
468
|
+
}
|
|
469
|
+
if (base != base) {
|
|
470
|
+
return (RgNum.nan())
|
|
471
|
+
}
|
|
472
|
+
def absBase:double (RgNum.abs(base))
|
|
473
|
+
def expIsInt:boolean (RgNum.isInteger(expo))
|
|
474
|
+
def expOdd:boolean false
|
|
475
|
+
if expIsInt {
|
|
476
|
+
def half:double (expo / 2.0)
|
|
477
|
+
expOdd = ((RgNum.trunc(half)) != half)
|
|
478
|
+
}
|
|
479
|
+
; --- an infinite exponent ---
|
|
480
|
+
if (expo == i) {
|
|
481
|
+
if (absBase == 1.0) {
|
|
482
|
+
return (RgNum.nan())
|
|
483
|
+
}
|
|
484
|
+
if (absBase > 1.0) {
|
|
485
|
+
return i
|
|
486
|
+
}
|
|
487
|
+
return 0.0
|
|
488
|
+
}
|
|
489
|
+
if (expo == (0.0 - i)) {
|
|
490
|
+
if (absBase == 1.0) {
|
|
491
|
+
return (RgNum.nan())
|
|
492
|
+
}
|
|
493
|
+
if (absBase > 1.0) {
|
|
494
|
+
return 0.0
|
|
495
|
+
}
|
|
496
|
+
return i
|
|
497
|
+
}
|
|
498
|
+
; --- an infinite base ---
|
|
499
|
+
if (base == i) {
|
|
500
|
+
if (expo > 0.0) {
|
|
501
|
+
return i
|
|
502
|
+
}
|
|
503
|
+
return 0.0
|
|
504
|
+
}
|
|
505
|
+
if (base == (0.0 - i)) {
|
|
506
|
+
if (expo > 0.0) {
|
|
507
|
+
if (expIsInt && expOdd) {
|
|
508
|
+
return (0.0 - i)
|
|
509
|
+
}
|
|
510
|
+
return i
|
|
511
|
+
}
|
|
512
|
+
if (expIsInt && expOdd) {
|
|
513
|
+
return (RgNum.negZero())
|
|
514
|
+
}
|
|
515
|
+
return 0.0
|
|
516
|
+
}
|
|
517
|
+
; --- a zero base. The sign of the zero survives an odd integer power.
|
|
518
|
+
if (base == 0.0) {
|
|
519
|
+
def negBase:boolean (RgNum.isNegZero(base))
|
|
520
|
+
if (expo > 0.0) {
|
|
521
|
+
if (negBase && (expIsInt && expOdd)) {
|
|
522
|
+
return (RgNum.negZero())
|
|
523
|
+
}
|
|
524
|
+
return 0.0
|
|
525
|
+
}
|
|
526
|
+
if (negBase && (expIsInt && expOdd)) {
|
|
527
|
+
return (0.0 - i)
|
|
528
|
+
}
|
|
529
|
+
return i
|
|
530
|
+
}
|
|
531
|
+
; --- a negative base is only defined for an integer exponent ---
|
|
532
|
+
if (base < 0.0) {
|
|
533
|
+
if (false == expIsInt) {
|
|
534
|
+
return (RgNum.nan())
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
; An integer exponent small enough to square out is done exactly, which
|
|
538
|
+
; keeps pow(10, 21) and the like off the series path.
|
|
539
|
+
if expIsInt {
|
|
540
|
+
def av:double (RgNum.abs(expo))
|
|
541
|
+
if (av <= 1024.0) {
|
|
542
|
+
return (RgNum.powInt(base (to_int expo)))
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
; The general case, through the identity. Only reachable with a positive
|
|
546
|
+
; base and a fractional or very large exponent.
|
|
547
|
+
def r:double (RgNum.exp((expo * (RgNum.log(absBase)))))
|
|
548
|
+
return r
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
static sfn sqrtD:double (v:double) {
|
|
552
|
+
if (v != v) {
|
|
553
|
+
return v
|
|
554
|
+
}
|
|
555
|
+
if (v < 0.0) {
|
|
556
|
+
return (RgNum.nan())
|
|
557
|
+
}
|
|
558
|
+
; ±0 and +Infinity are their own square roots, and the operator agrees;
|
|
559
|
+
; the guard is here so -0 keeps its sign on every target.
|
|
560
|
+
if (v == 0.0) {
|
|
561
|
+
return v
|
|
562
|
+
}
|
|
563
|
+
return (sqrt v)
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
; ---- 32-bit coercions --------------------------------------------------
|
|
567
|
+
; ECMA-262 ToInt32 / ToUint32, in the double domain. These are what the
|
|
568
|
+
; bitwise operators are specified over, and RgU32 assumes them.
|
|
569
|
+
|
|
570
|
+
static sfn toUint32:double (v:double) {
|
|
571
|
+
if (v != v) {
|
|
572
|
+
return 0.0
|
|
573
|
+
}
|
|
574
|
+
if (false == (RgNum.isFinite(v))) {
|
|
575
|
+
return 0.0
|
|
576
|
+
}
|
|
577
|
+
def two32:double 4294967296.0
|
|
578
|
+
def t:double (RgNum.trunc(v))
|
|
579
|
+
def m:double (t - (two32 * (RgNum.floorD((t / two32)))))
|
|
580
|
+
if (m < 0.0) {
|
|
581
|
+
m = (m + two32)
|
|
582
|
+
}
|
|
583
|
+
return m
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
static sfn toInt32:double (v:double) {
|
|
587
|
+
def u:double (RgNum.toUint32(v))
|
|
588
|
+
def two31:double 2147483648.0
|
|
589
|
+
if (u >= two31) {
|
|
590
|
+
return (u - 4294967296.0)
|
|
591
|
+
}
|
|
592
|
+
return u
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
; ---- float32 -----------------------------------------------------------
|
|
596
|
+
; The nearest value representable as a float32. Ranger has no float32 type,
|
|
597
|
+
; so the round trip is done by hand: normalise the magnitude into [1, 2),
|
|
598
|
+
; round the 24-bit mantissa, scale back. A magnitude past the float32 range
|
|
599
|
+
; saturates to Infinity.
|
|
600
|
+
static sfn toFloat32:double (v:double) {
|
|
601
|
+
if (v != v) {
|
|
602
|
+
return v
|
|
603
|
+
}
|
|
604
|
+
if (v == 0.0) {
|
|
605
|
+
return v
|
|
606
|
+
}
|
|
607
|
+
; An infinity must leave before the normalising loops: inf/2 is still
|
|
608
|
+
; inf, so `while (m >= 2)` would never terminate.
|
|
609
|
+
if (false == (RgNum.isFinite(v))) {
|
|
610
|
+
return v
|
|
611
|
+
}
|
|
612
|
+
def neg:boolean (v < 0.0)
|
|
613
|
+
def m:double v
|
|
614
|
+
if neg {
|
|
615
|
+
m = (0.0 - v)
|
|
616
|
+
}
|
|
617
|
+
def e:int 0
|
|
618
|
+
while (m >= 2.0) {
|
|
619
|
+
m = (m / 2.0)
|
|
620
|
+
e = (e + 1)
|
|
621
|
+
}
|
|
622
|
+
while (m < 1.0) {
|
|
623
|
+
m = (m * 2.0)
|
|
624
|
+
e = (e - 1)
|
|
625
|
+
}
|
|
626
|
+
; float32 carries 24 mantissa bits and an exponent in [-126, 127].
|
|
627
|
+
if (e > 127) {
|
|
628
|
+
if neg {
|
|
629
|
+
return (0.0 - (RgNum.inf()))
|
|
630
|
+
}
|
|
631
|
+
return (RgNum.inf())
|
|
632
|
+
}
|
|
633
|
+
def scaleBits:double 8388608.0
|
|
634
|
+
def shifted:double (m * scaleBits)
|
|
635
|
+
def rounded:double (RgNum.round(shifted))
|
|
636
|
+
if (rounded >= 16777216.0) {
|
|
637
|
+
rounded = (rounded / 2.0)
|
|
638
|
+
e = (e + 1)
|
|
639
|
+
if (e > 127) {
|
|
640
|
+
if neg {
|
|
641
|
+
return (0.0 - (RgNum.inf()))
|
|
642
|
+
}
|
|
643
|
+
return (RgNum.inf())
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
def mant:double (rounded / scaleBits)
|
|
647
|
+
def out:double (mant * (RgNum.powInt(2.0 e)))
|
|
648
|
+
if neg {
|
|
649
|
+
return (0.0 - out)
|
|
650
|
+
}
|
|
651
|
+
return out
|
|
652
|
+
}
|
|
653
|
+
}
|