ranger-compiler 3.2.0 → 3.5.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.
Files changed (61) hide show
  1. package/CHANGELOG.md +2463 -0
  2. package/LICENSE +28 -0
  3. package/LICENSE-MIT +21 -0
  4. package/README.md +1651 -1895
  5. package/dist/Lang.rgr +10946 -5852
  6. package/dist/README.md +3 -2
  7. package/dist/api.d.ts +1548 -27
  8. package/dist/api.js +70572 -38295
  9. package/dist/git-http.mjs +126 -0
  10. package/dist/lib/ACEEditor.rgr +2 -0
  11. package/dist/lib/Ajax.rgr +2 -0
  12. package/dist/lib/CmdParams.rgr +2 -0
  13. package/dist/lib/Crypto.rgr +2 -0
  14. package/dist/lib/DOMLib.rgr +2 -0
  15. package/dist/lib/Engine3D.rgr +2 -0
  16. package/dist/lib/ImmutableVector.rgr +2 -0
  17. package/dist/lib/IndexedDB.rgr +2 -0
  18. package/dist/lib/IsoDate/DateMath.rgr +2 -0
  19. package/dist/lib/IsoDate/IsoCalendar.rgr +2 -0
  20. package/dist/lib/IsoDate/IsoDateParse.rgr +2 -0
  21. package/dist/lib/IsoDateLib.rgr +2 -0
  22. package/dist/lib/JSON.rgr +4906 -48
  23. package/dist/lib/JinxProcess.rgr +2 -0
  24. package/dist/lib/RangerProcess.rgr +2 -0
  25. package/dist/lib/Regex/RegexMatch.rgr +2 -0
  26. package/dist/lib/RegexLib.rgr +2 -0
  27. package/dist/lib/SQL.rgr +2 -0
  28. package/dist/lib/ServiceLib.rgr +2 -0
  29. package/dist/lib/Shell.rgr +326 -0
  30. package/dist/lib/Storage.rgr +2 -0
  31. package/dist/lib/Time.rgr +2 -0
  32. package/dist/lib/Timers.rgr +2 -0
  33. package/dist/lib/TypedArrays.rgr +2 -0
  34. package/dist/lib/ViewLib.rgr +2 -0
  35. package/dist/lib/WebLib.rgr +2 -0
  36. package/dist/lib/WebServerLib.rgr +2 -0
  37. package/dist/lib/apple/AppleAppBuilder.rgr +572 -0
  38. package/dist/lib/apple/AppleAppSpec.rgr +201 -0
  39. package/dist/lib/apple/AppleDevice.rgr +295 -0
  40. package/dist/lib/apple/AppleDeviceDoctor.rgr +453 -0
  41. package/dist/lib/apple/AppleSigning.rgr +379 -0
  42. package/dist/lib/apple/AppleSimulator.rgr +248 -0
  43. package/dist/lib/apple/AppleTarget.rgr +185 -0
  44. package/dist/lib/apple/AppleToolchain.rgr +458 -0
  45. package/dist/lib/apple/README.md +311 -0
  46. package/dist/lib/apple/apple_test.rgr +669 -0
  47. package/dist/lib/core/README.md +251 -0
  48. package/dist/lib/core/RgBase.rgr +313 -0
  49. package/dist/lib/core/RgNum.rgr +653 -0
  50. package/dist/lib/core/RgText.rgr +680 -0
  51. package/dist/lib/core/RgU32.rgr +309 -0
  52. package/dist/lib/ranger-dir.rgr +2 -0
  53. package/dist/lib/shell_test.rgr +193 -0
  54. package/dist/lib/stdlib.rgr +1176 -665
  55. package/dist/lib/stdops.rgr +2 -0
  56. package/dist/lib/zip/Inflate.rgr +675 -0
  57. package/dist/lib/zip/ZipBuffer.rgr +358 -0
  58. package/dist/package.json +1 -1
  59. package/dist/rgrc.js +76333 -40647
  60. package/dist/stdops.rgr +2 -0
  61. package/package.json +1121 -339
@@ -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
+ }