@grain/stdlib 0.5.3 → 0.5.5

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 (77) hide show
  1. package/CHANGELOG.md +61 -0
  2. package/array.gr +65 -57
  3. package/array.md +54 -6
  4. package/buffer.gr +71 -1
  5. package/buffer.md +142 -0
  6. package/bytes.gr +52 -3
  7. package/bytes.md +117 -0
  8. package/char.gr +23 -20
  9. package/char.md +18 -3
  10. package/immutablemap.gr +493 -0
  11. package/immutablemap.md +479 -0
  12. package/immutablepriorityqueue.gr +44 -16
  13. package/immutablepriorityqueue.md +44 -1
  14. package/immutableset.gr +498 -0
  15. package/immutableset.md +449 -0
  16. package/int32.gr +39 -37
  17. package/int32.md +6 -0
  18. package/int64.gr +39 -37
  19. package/int64.md +6 -0
  20. package/list.gr +33 -24
  21. package/list.md +39 -10
  22. package/map.gr +19 -28
  23. package/marshal.gr +4 -4
  24. package/number.gr +727 -26
  25. package/number.md +345 -23
  26. package/option.gr +30 -26
  27. package/option.md +12 -0
  28. package/package.json +1 -1
  29. package/path.gr +787 -0
  30. package/path.md +727 -0
  31. package/pervasives.gr +3 -4
  32. package/pervasives.md +6 -1
  33. package/priorityqueue.gr +25 -5
  34. package/priorityqueue.md +30 -0
  35. package/queue.gr +22 -7
  36. package/queue.md +18 -1
  37. package/regex.gr +161 -65
  38. package/regex.md +70 -0
  39. package/result.gr +24 -20
  40. package/result.md +12 -0
  41. package/runtime/atof/common.gr +198 -0
  42. package/runtime/atof/common.md +243 -0
  43. package/runtime/atof/decimal.gr +663 -0
  44. package/runtime/atof/decimal.md +59 -0
  45. package/runtime/atof/lemire.gr +264 -0
  46. package/runtime/atof/lemire.md +6 -0
  47. package/runtime/atof/parse.gr +615 -0
  48. package/runtime/atof/parse.md +12 -0
  49. package/runtime/atof/slow.gr +238 -0
  50. package/runtime/atof/slow.md +6 -0
  51. package/runtime/atof/table.gr +2016 -0
  52. package/runtime/atof/table.md +12 -0
  53. package/runtime/{stringUtils.gr → atoi/parse.gr} +1 -1
  54. package/runtime/{stringUtils.md → atoi/parse.md} +1 -1
  55. package/runtime/bigint.gr +7 -7
  56. package/runtime/compare.gr +2 -1
  57. package/runtime/equal.gr +3 -2
  58. package/runtime/exception.gr +9 -5
  59. package/runtime/exception.md +8 -2
  60. package/runtime/gc.gr +2 -1
  61. package/runtime/malloc.gr +1 -3
  62. package/runtime/numberUtils.gr +13 -13
  63. package/runtime/numberUtils.md +6 -0
  64. package/runtime/numbers.gr +123 -39
  65. package/runtime/numbers.md +26 -0
  66. package/runtime/string.gr +4 -2
  67. package/runtime/unsafe/conv.gr +21 -41
  68. package/runtime/unsafe/conv.md +0 -3
  69. package/runtime/unsafe/printWasm.gr +4 -40
  70. package/runtime/utils/printing.gr +3 -3
  71. package/set.gr +25 -25
  72. package/stack.gr +14 -0
  73. package/stack.md +17 -0
  74. package/string.gr +313 -39
  75. package/string.md +99 -0
  76. package/sys/file.gr +1 -1
  77. package/sys/time.gr +4 -4
@@ -0,0 +1,615 @@
1
+ // This module was based on Rust's dec2flt
2
+ // https://github.com/rust-lang/rust/blob/1cbc45942d5c0f6eb5d94e3b10762ba541958035/library/core/src/num/dec2flt/parse.rs
3
+ // Rust's MIT license is provided below:
4
+ /*
5
+ * Permission is hereby granted, free of charge, to any
6
+ * person obtaining a copy of this software and associated
7
+ * documentation files (the "Software"), to deal in the
8
+ * Software without restriction, including without
9
+ * limitation the rights to use, copy, modify, merge,
10
+ * publish, distribute, sublicense, and/or sell copies of
11
+ * the Software, and to permit persons to whom the Software
12
+ * is furnished to do so, subject to the following
13
+ * conditions:
14
+ *
15
+ * The above copyright notice and this permission notice
16
+ * shall be included in all copies or substantial portions
17
+ * of the Software.
18
+ *
19
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
20
+ * ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
21
+ * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
22
+ * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
23
+ * SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
24
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
26
+ * IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27
+ * DEALINGS IN THE SOFTWARE.
28
+ */
29
+
30
+ import WasmI32 from "runtime/unsafe/wasmi32"
31
+ import WasmI64 from "runtime/unsafe/wasmi64"
32
+ import WasmF64 from "runtime/unsafe/wasmf64"
33
+ import { newInt32, newInt64, newFloat64 } from "runtime/dataStructures"
34
+
35
+ import {
36
+ _CHAR_CODE_UNDERSCORE,
37
+ _CHAR_CODE_PLUS,
38
+ _CHAR_CODE_MINUS,
39
+ _CHAR_CODE_DOT,
40
+ _CHAR_CODE_0,
41
+ _CHAR_CODE_E,
42
+ _CHAR_CODE_e,
43
+ _CHAR_CODE_A,
44
+ _CHAR_CODE_Z,
45
+ _CHAR_CODE_a,
46
+ _CHAR_CODE_f,
47
+ _CHAR_CODE_i,
48
+ _CHAR_CODE_n,
49
+ _CHAR_CODE_t,
50
+ _CHAR_CODE_y,
51
+ _MIN_EXPONENT_FAST_PATH,
52
+ _MAX_EXPONENT_DISGUISED_FAST_PATH,
53
+ _MAX_EXPONENT_FAST_PATH,
54
+ _MAX_MANTISSA_FAST_PATH,
55
+ is8Digits,
56
+ getPowers10,
57
+ getPowers10FastPath,
58
+ biasedFpToNumber,
59
+ fpNan,
60
+ fpInf,
61
+ } from "./common"
62
+
63
+ import { computeFloat } from "./lemire"
64
+ import { parseLongMantissa } from "./slow"
65
+
66
+ // Try to parse 8 digits at a time:
67
+ // https://johnnylee-sde.github.io/Fast-numeric-string-to-int/
68
+ @unsafe
69
+ let parse8Digits = (digits: WasmI64) => {
70
+ let (+) = WasmI64.add
71
+ let (-) = WasmI64.sub
72
+ let (*) = WasmI64.mul
73
+ let (>>) = WasmI64.shrU
74
+ let (&) = WasmI64.and
75
+ let (|) = WasmI64.or
76
+ let (==) = WasmI64.eq
77
+
78
+ let _MASK = 0x0000_00FF_0000_00FFN
79
+
80
+ let a = digits - 0x3030_3030_3030_3030N
81
+ let b = a * 10N + (a >> 8N)
82
+ let c = (b & _MASK) * 0x000F_4240_0000_0064N
83
+ let d = (b >> 16N & _MASK) * 0x0000_2710_0000_0001N
84
+
85
+ (c + d) >> 32N
86
+ }
87
+
88
+ @unsafe
89
+ let parseScientificExponent = (ptr: WasmI32, offset: WasmI32, len: WasmI32) => {
90
+ let (+) = WasmI32.add
91
+ let (-) = WasmI32.sub
92
+ let (>=) = WasmI32.geU
93
+ let (<) = WasmI32.ltU
94
+ let (==) = WasmI32.eq
95
+ let (!=) = WasmI32.ne
96
+
97
+ let mut offset = offset
98
+ let mut exponent = 0n
99
+ let mut negative = false
100
+
101
+ // Parse the optional leading sign
102
+ match (WasmI32.load8U(ptr, offset)) {
103
+ fst when fst == _CHAR_CODE_PLUS => {
104
+ offset += 1n
105
+ negative = false
106
+ },
107
+ fst when fst == _CHAR_CODE_MINUS => {
108
+ offset += 1n
109
+ negative = true
110
+ },
111
+ _ => void,
112
+ }
113
+
114
+ if (offset >= len) {
115
+ Err("Invalid exponent")
116
+ } else {
117
+ // Parse digits '0'-'9' until we hit a non-digit byte or reach the end of the string
118
+ while (offset < len) {
119
+ let char = WasmI32.load8U(ptr, offset)
120
+ let digit = char - 0x30n
121
+ if (digit >= 0n && digit < 10n) {
122
+ if (WasmI32.ltS(exponent, 0x10000n)) {
123
+ exponent = WasmI32.add(digit, WasmI32.mul(10n, exponent))
124
+ }
125
+ offset += 1n
126
+ } else if (char == _CHAR_CODE_UNDERSCORE) {
127
+ offset += 1n
128
+ } else {
129
+ break
130
+ }
131
+ }
132
+ if (offset != len) {
133
+ Err("Invalid exponent")
134
+ } else if (negative) {
135
+ Ok(WasmI32.toGrain(newInt32(WasmI32.sub(0n, exponent))): Int32)
136
+ } else {
137
+ Ok(WasmI32.toGrain(newInt32(exponent)): Int32)
138
+ }
139
+ }
140
+ }
141
+
142
+ @unsafe
143
+ let parseFloatToParts = (string: String) => {
144
+ let (+) = WasmI32.add
145
+ let (-) = WasmI32.sub
146
+ let (>) = WasmI32.gtU
147
+ let (>=) = WasmI32.geU
148
+ let (<) = WasmI32.ltU
149
+ let (==) = WasmI32.eq
150
+ let (!=) = WasmI32.ne
151
+
152
+ let ptr = WasmI32.fromGrain(string)
153
+ match (WasmI32.load(ptr, 4n)) {
154
+ // Invalid string
155
+ 0n => Err("Invalid string"),
156
+ // Continue to parse the string
157
+ len => {
158
+ let ptr = ptr + 8n
159
+
160
+ let mut i = 0n
161
+ let mut mantissa = 0N
162
+ let mut exponent = 0n
163
+ let mut numDigits = 0n
164
+ let mut numDigitsAfterDot = 0n
165
+ let mut manyDigits = false
166
+
167
+ // Parse the optional leading sign
168
+ let mut negative = false
169
+ match (WasmI32.load8U(ptr, i)) {
170
+ fst when fst == _CHAR_CODE_PLUS => {
171
+ i += 1n
172
+ negative = false
173
+ },
174
+ fst when fst == _CHAR_CODE_MINUS => {
175
+ i += 1n
176
+ negative = true
177
+ },
178
+ _ => void,
179
+ }
180
+
181
+ // Parse digits '0'-'9' until we hit a non-digit byte or reach the end of the string
182
+ while (i < len) {
183
+ let char = WasmI32.load8U(ptr, i)
184
+ let digit = char - _CHAR_CODE_0
185
+ if (digit >= 0n && digit < 10n) {
186
+ mantissa = WasmI64.add(
187
+ WasmI64.mul(mantissa, 10N),
188
+ WasmI64.extendI32U(digit)
189
+ )
190
+ numDigits += 1n
191
+ i += 1n
192
+ } else if (char == _CHAR_CODE_UNDERSCORE) {
193
+ i += 1n
194
+ } else {
195
+ break
196
+ }
197
+ }
198
+
199
+ let digitsBeforeDecimal = numDigits
200
+
201
+ // Handle the dot
202
+ match (WasmI32.load8U(ptr, i)) {
203
+ c when c == _CHAR_CODE_DOT => {
204
+ i += 1n
205
+ let dotStartIndex = i
206
+ // Parse chunks of 8 digits
207
+ while (WasmI32.leU(i + 8n, len)) {
208
+ let digits = WasmI64.load(ptr, i)
209
+ if (is8Digits(digits)) {
210
+ mantissa = WasmI64.add(
211
+ parse8Digits(digits),
212
+ WasmI64.mul(mantissa, 100000000N)
213
+ )
214
+ i += 8n
215
+ numDigitsAfterDot += 8n
216
+ } else {
217
+ break
218
+ }
219
+ }
220
+ // Parse digits '0'-'9' until we hit a non-digit byte or reach the end of the string
221
+ while (i < len) {
222
+ let char = WasmI32.load8U(ptr, i)
223
+ let digit = char - _CHAR_CODE_0
224
+ if (digit >= 0n && digit < 10n) {
225
+ mantissa = WasmI64.add(
226
+ WasmI64.extendI32U(digit),
227
+ WasmI64.mul(mantissa, 10N)
228
+ )
229
+ i += 1n
230
+ numDigitsAfterDot += 1n
231
+ } else if (char == _CHAR_CODE_UNDERSCORE) {
232
+ i += 1n
233
+ } else {
234
+ break
235
+ }
236
+ }
237
+ exponent = 0n - numDigitsAfterDot
238
+ },
239
+ _ => void,
240
+ }
241
+
242
+ numDigits += numDigitsAfterDot
243
+
244
+ match (numDigits) {
245
+ n when n == 0n => Err("Invalid float"),
246
+ _ => {
247
+ let _MAX_MANTISSA_DIGITS = 19n // 10^19 fits in uint64
248
+ let _MIN_19DIGIT_INT = 100_0000_0000_0000_0000N
249
+
250
+ // Parse scientific notation
251
+ let exponentResult = match (WasmI32.load8U(ptr, i)) {
252
+ c when c == _CHAR_CODE_E || c == _CHAR_CODE_e => {
253
+ i += 1n
254
+ parseScientificExponent(ptr, i, len)
255
+ },
256
+ _ => {
257
+ if (i != len) {
258
+ Err("Invalid float")
259
+ } else {
260
+ Ok(0l)
261
+ }
262
+ },
263
+ }
264
+
265
+ match (exponentResult) {
266
+ Ok(exponentNumber) => {
267
+ let exponentNumber = WasmI32.load(
268
+ WasmI32.fromGrain(exponentNumber),
269
+ 8n
270
+ )
271
+ exponent += exponentNumber
272
+
273
+ // Check to see if we need to truncate
274
+ if (numDigits > _MAX_MANTISSA_DIGITS) {
275
+ manyDigits = false
276
+ numDigits -= _MAX_MANTISSA_DIGITS
277
+ let mut i = 0n
278
+ while (i < len) {
279
+ let c = WasmI32.load8U(ptr, i)
280
+ if (c == _CHAR_CODE_DOT || c == _CHAR_CODE_0) {
281
+ let n = c - (_CHAR_CODE_0 - 1n)
282
+ numDigits -= if (WasmI32.ltS(n, 0n)) {
283
+ 0n
284
+ } else if (WasmI32.gtS(n, 255n)) {
285
+ 255n
286
+ } else {
287
+ n
288
+ }
289
+ i += 1n
290
+ } else if (c == _CHAR_CODE_UNDERSCORE) {
291
+ continue
292
+ } else {
293
+ break
294
+ }
295
+ }
296
+
297
+ // Attempt to parse 19 digits, the most that can fit in an i64
298
+ let mut digitsParsed = 0n
299
+ if (WasmI32.gtS(numDigits, 0n)) {
300
+ manyDigits = true
301
+ mantissa = 0N
302
+
303
+ let mut i = 0n
304
+ while (WasmI64.ltU(mantissa, _MIN_19DIGIT_INT)) {
305
+ if (i < len) {
306
+ let char = WasmI32.load8U(ptr, i)
307
+ let digit = char - _CHAR_CODE_0
308
+ if (digit >= 0n && digit < 10n) {
309
+ mantissa = WasmI64.add(
310
+ WasmI64.mul(mantissa, 10N),
311
+ WasmI64.extendI32U(digit)
312
+ )
313
+ i += 1n
314
+ digitsParsed += 1n
315
+ } else if (char == _CHAR_CODE_UNDERSCORE) {
316
+ i += 1n
317
+ } else {
318
+ break
319
+ }
320
+ } else {
321
+ break
322
+ }
323
+ }
324
+
325
+ exponent = if (WasmI64.geU(mantissa, _MIN_19DIGIT_INT)) {
326
+ digitsBeforeDecimal - digitsParsed
327
+ } else {
328
+ // From Rust: https://github.com/rust-lang/rust/blob/e960b5e7749e95c6a6b2fdec7250a48105664efb/library/core/src/num/dec2flt/parse.rs#L179
329
+ // the next byte must be present and be '.'
330
+ // We know this is true because we had more than 19
331
+ // digits previously, so we overflowed a 64-bit integer,
332
+ // but parsing only the integral digits produced less
333
+ // than 19 digits. That means we must have a decimal
334
+ // point, and at least 1 fractional digit.
335
+ i += 1n
336
+ let mut fractionalDigitsParsed = 0n
337
+ while (WasmI64.ltU(mantissa, _MIN_19DIGIT_INT)) {
338
+ if (i < len) {
339
+ let char = WasmI32.load8U(ptr, i)
340
+ let digit = char - _CHAR_CODE_0
341
+ if (digit < 10n) {
342
+ mantissa = WasmI64.add(
343
+ WasmI64.mul(mantissa, 10N),
344
+ WasmI64.extendI32U(digit)
345
+ )
346
+ i += 1n
347
+ fractionalDigitsParsed += 1n
348
+ } else if (char == _CHAR_CODE_UNDERSCORE) {
349
+ i += 1n
350
+ } else {
351
+ break
352
+ }
353
+ } else {
354
+ break
355
+ }
356
+ }
357
+ 0n - fractionalDigitsParsed
358
+ }
359
+ // Add back the explicit part
360
+ exponent += exponentNumber
361
+ }
362
+ Ok(
363
+ (
364
+ WasmI32.toGrain(newInt32(exponent)): Int32,
365
+ WasmI32.toGrain(newInt64(mantissa)): Int64,
366
+ negative,
367
+ manyDigits,
368
+ )
369
+ )
370
+ } else {
371
+ Ok(
372
+ (
373
+ WasmI32.toGrain(newInt32(exponent)): Int32,
374
+ WasmI32.toGrain(newInt64(mantissa)): Int64,
375
+ negative,
376
+ manyDigits,
377
+ )
378
+ )
379
+ }
380
+ },
381
+ Err(err) => Err(err),
382
+ }
383
+ },
384
+ }
385
+ },
386
+ }
387
+ }
388
+
389
+ @unsafe
390
+ let parseInfNan = s => {
391
+ let (+) = WasmI32.add
392
+ let (-) = WasmI32.sub
393
+ let (<) = WasmI32.ltS
394
+ let (>=) = WasmI32.geS
395
+ let (<=) = WasmI32.leS
396
+ let (==) = WasmI32.eq
397
+
398
+ let ptr = WasmI32.fromGrain(s)
399
+ match (WasmI32.load(ptr, 4n)) {
400
+ // Invalid string
401
+ 0n => Err("Invalid string"),
402
+ // Continue to parse
403
+ len => {
404
+ let ptr = ptr + 8n
405
+
406
+ let mut i = 0n
407
+ let mut mantissa = 0N
408
+ let mut exponent = 0n
409
+ let mut numDigits = 0n
410
+ let mut numDigitsAfterDot = 0n
411
+ let mut manyDigits = false
412
+
413
+ // Parse the optional leading sign
414
+ let mut negative = false
415
+ match (WasmI32.load8U(ptr, i)) {
416
+ fst when fst == _CHAR_CODE_PLUS => {
417
+ i += 1n
418
+ negative = false
419
+ },
420
+ fst when fst == _CHAR_CODE_MINUS => {
421
+ i += 1n
422
+ negative = true
423
+ },
424
+ _ => void,
425
+ }
426
+
427
+ if (len - i < 3n) {
428
+ Err("Invalid string")
429
+ } else {
430
+ let mut c1 = WasmI32.load8U(ptr + i, 0n)
431
+ if (c1 >= _CHAR_CODE_A && c1 <= _CHAR_CODE_Z) {
432
+ c1 += 0x20n
433
+ }
434
+ let mut c2 = WasmI32.load8U(ptr + i, 1n)
435
+ if (c2 >= _CHAR_CODE_A && c2 <= _CHAR_CODE_Z) {
436
+ c2 += 0x20n
437
+ }
438
+ let mut c3 = WasmI32.load8U(ptr + i, 2n)
439
+ if (c3 >= _CHAR_CODE_A && c3 <= _CHAR_CODE_Z) {
440
+ c3 += 0x20n
441
+ }
442
+
443
+ if (
444
+ len - i == 3n &&
445
+ c1 == _CHAR_CODE_n &&
446
+ c2 == _CHAR_CODE_a &&
447
+ c3 == _CHAR_CODE_n
448
+ ) {
449
+ Ok((fpNan(), negative))
450
+ } else if (
451
+ c1 == _CHAR_CODE_i && c2 == _CHAR_CODE_n && c3 == _CHAR_CODE_f
452
+ ) {
453
+ if (len - i == 3n) {
454
+ Ok((fpInf(), negative))
455
+ } else if (len - i == 8n) {
456
+ let mut c4 = WasmI32.load8U(ptr + i, 3n)
457
+ if (c4 >= _CHAR_CODE_A && c4 <= _CHAR_CODE_Z) {
458
+ c4 += 0x20n
459
+ }
460
+ let mut c5 = WasmI32.load8U(ptr + i, 4n)
461
+ if (c5 >= _CHAR_CODE_A && c5 <= _CHAR_CODE_Z) {
462
+ c5 += 0x20n
463
+ }
464
+ let mut c6 = WasmI32.load8U(ptr + i, 5n)
465
+ if (c6 >= _CHAR_CODE_A && c6 <= _CHAR_CODE_Z) {
466
+ c6 += 0x20n
467
+ }
468
+ let mut c7 = WasmI32.load8U(ptr + i, 6n)
469
+ if (c7 >= _CHAR_CODE_A && c7 <= _CHAR_CODE_Z) {
470
+ c7 += 0x20n
471
+ }
472
+ let mut c8 = WasmI32.load8U(ptr + i, 7n)
473
+ if (c8 >= _CHAR_CODE_A && c8 <= _CHAR_CODE_Z) {
474
+ c8 += 0x20n
475
+ }
476
+
477
+ if (
478
+ c4 == _CHAR_CODE_i &&
479
+ c5 == _CHAR_CODE_n &&
480
+ c6 == _CHAR_CODE_i &&
481
+ c7 == _CHAR_CODE_t &&
482
+ c8 == _CHAR_CODE_y
483
+ ) {
484
+ Ok((fpInf(), negative))
485
+ } else {
486
+ Err("Invalid string")
487
+ }
488
+ } else {
489
+ Err("Invalid string")
490
+ }
491
+ } else {
492
+ Err("Invalid string")
493
+ }
494
+ }
495
+ },
496
+ }
497
+ }
498
+
499
+ @unsafe
500
+ export let isFastPath =
501
+ (
502
+ exponent: WasmI32,
503
+ mantissa: WasmI64,
504
+ negative: Bool,
505
+ manyDigits: Bool,
506
+ ) => {
507
+ let (<=) = WasmI32.leS
508
+ let (<<) = WasmI64.shl
509
+
510
+ _MIN_EXPONENT_FAST_PATH <= exponent &&
511
+ exponent <= _MAX_EXPONENT_DISGUISED_FAST_PATH &&
512
+ WasmI64.leU(mantissa, _MAX_MANTISSA_FAST_PATH) &&
513
+ !manyDigits
514
+ }
515
+
516
+ @unsafe
517
+ export let parseFloat = (string: String) => {
518
+ let (!=) = WasmI32.ne
519
+
520
+ match (parseFloatToParts(string)) {
521
+ Ok((exponent, mantissa, negative, manyDigits)) => {
522
+ let exponent = WasmI32.load(WasmI32.fromGrain(exponent), 8n)
523
+ let mantissa = WasmI64.load(WasmI32.fromGrain(mantissa), 8n)
524
+ let floatOpt = if (isFastPath(exponent, mantissa, negative, manyDigits)) {
525
+ let (<=) = WasmI32.leS
526
+ if (exponent <= _MAX_EXPONENT_FAST_PATH) {
527
+ // normal fast path
528
+ let (*) = WasmF64.mul
529
+ let (/) = WasmF64.div
530
+ let (<) = WasmI32.leS
531
+
532
+ let mantissa = WasmF64.convertI64U(mantissa)
533
+ let n = if (exponent < 0n) {
534
+ mantissa / getPowers10FastPath(WasmI32.mul(exponent, -1n))
535
+ } else {
536
+ mantissa * getPowers10FastPath(exponent)
537
+ }
538
+ if (negative) {
539
+ Some(WasmI32.toGrain(newFloat64(n * -1.W)): Number)
540
+ } else {
541
+ Some(WasmI32.toGrain(newFloat64(n)): Number)
542
+ }
543
+ } else {
544
+ // disguised fast path
545
+ let (-) = WasmI32.sub
546
+ let (*) = WasmF64.mul
547
+ let (>) = WasmI64.gtS
548
+
549
+ let shift = exponent - _MAX_EXPONENT_FAST_PATH
550
+
551
+ let mantissa = WasmI64.mul(
552
+ mantissa,
553
+ WasmI64.extendI32U(getPowers10(shift))
554
+ )
555
+ if (mantissa > _MAX_MANTISSA_FAST_PATH) {
556
+ None
557
+ } else {
558
+ assert WasmI64.leS(mantissa, _MAX_MANTISSA_FAST_PATH)
559
+
560
+ let mantissa = WasmF64.convertI64U(mantissa)
561
+ let n = mantissa * getPowers10FastPath(_MAX_EXPONENT_FAST_PATH)
562
+ if (negative) {
563
+ Some(WasmI32.toGrain(newFloat64(n * -1.W)): Number)
564
+ } else {
565
+ Some(WasmI32.toGrain(newFloat64(n)): Number)
566
+ }
567
+ }
568
+ }
569
+ } else {
570
+ None
571
+ }
572
+ // Check if fast path worked
573
+ match (floatOpt) {
574
+ Some(n) => Ok(n),
575
+ None => {
576
+ // From Rust:
577
+ // If significant digits were truncated, then we can have rounding error
578
+ // only if `mantissa + 1` produces a different result. We also avoid
579
+ // redundantly using the Eisel-Lemire algorithm if it was unable to
580
+ // correctly round on the first pass.
581
+
582
+ let mut fp = computeFloat(WasmI64.extendI32S(exponent), mantissa)
583
+ let f = WasmI64.load(WasmI32.fromGrain(fp.f), 8n)
584
+ let e = WasmI32.load(WasmI32.fromGrain(fp.e), 8n)
585
+ if (manyDigits && WasmI32.geS(e, 0n)) {
586
+ let fp2 = computeFloat(
587
+ WasmI64.extendI32S(exponent),
588
+ WasmI64.add(mantissa, 1N)
589
+ )
590
+ let f2 = WasmI64.load(WasmI32.fromGrain(fp2.f), 8n)
591
+ let e2 = WasmI32.load(WasmI32.fromGrain(fp2.e), 8n)
592
+ if (e != e2 || WasmI64.ne(f, f2)) {
593
+ fp.e = -1l
594
+ }
595
+ }
596
+
597
+ // Unable to correctly round the float using the Eisel-Lemire algorithm.
598
+ // Fallback to a slower, but always correct algorithm.
599
+ let e = WasmI32.load(WasmI32.fromGrain(fp.e), 8n)
600
+ if (WasmI32.ltS(e, 0n)) {
601
+ fp = parseLongMantissa(string)
602
+ }
603
+
604
+ Ok(biasedFpToNumber(fp, negative))
605
+ },
606
+ }
607
+ },
608
+ Err(str) => {
609
+ match (parseInfNan(string)) {
610
+ Ok((fp, negative)) => Ok(biasedFpToNumber(fp, negative)),
611
+ _ => Err(str),
612
+ }
613
+ },
614
+ }
615
+ }
@@ -0,0 +1,12 @@
1
+ ### Parse.**isFastPath**
2
+
3
+ ```grain
4
+ isFastPath : (WasmI32, WasmI64, Bool, Bool) -> Bool
5
+ ```
6
+
7
+ ### Parse.**parseFloat**
8
+
9
+ ```grain
10
+ parseFloat : String -> Result<Number, String>
11
+ ```
12
+