@grain/stdlib 0.5.12 → 0.6.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 (155) hide show
  1. package/CHANGELOG.md +200 -0
  2. package/LICENSE +1 -1
  3. package/README.md +25 -2
  4. package/array.gr +1512 -199
  5. package/array.md +2032 -94
  6. package/bigint.gr +239 -140
  7. package/bigint.md +450 -106
  8. package/buffer.gr +595 -102
  9. package/buffer.md +903 -145
  10. package/bytes.gr +401 -110
  11. package/bytes.md +551 -63
  12. package/char.gr +228 -49
  13. package/char.md +373 -7
  14. package/exception.gr +26 -12
  15. package/exception.md +29 -5
  16. package/float32.gr +130 -109
  17. package/float32.md +185 -57
  18. package/float64.gr +112 -99
  19. package/float64.md +185 -57
  20. package/hash.gr +47 -37
  21. package/hash.md +21 -3
  22. package/int16.gr +430 -0
  23. package/int16.md +618 -0
  24. package/int32.gr +200 -269
  25. package/int32.md +254 -289
  26. package/int64.gr +142 -225
  27. package/int64.md +254 -289
  28. package/int8.gr +511 -0
  29. package/int8.md +786 -0
  30. package/json.gr +2084 -0
  31. package/json.md +608 -0
  32. package/list.gr +120 -68
  33. package/list.md +125 -80
  34. package/map.gr +560 -57
  35. package/map.md +672 -56
  36. package/marshal.gr +239 -227
  37. package/marshal.md +36 -4
  38. package/number.gr +626 -676
  39. package/number.md +738 -153
  40. package/option.gr +33 -35
  41. package/option.md +58 -42
  42. package/package.json +2 -2
  43. package/path.gr +148 -187
  44. package/path.md +47 -96
  45. package/pervasives.gr +75 -416
  46. package/pervasives.md +85 -180
  47. package/priorityqueue.gr +433 -74
  48. package/priorityqueue.md +422 -54
  49. package/queue.gr +362 -80
  50. package/queue.md +433 -38
  51. package/random.gr +67 -75
  52. package/random.md +68 -40
  53. package/range.gr +135 -63
  54. package/range.md +198 -43
  55. package/rational.gr +284 -0
  56. package/rational.md +545 -0
  57. package/regex.gr +933 -1066
  58. package/regex.md +59 -60
  59. package/result.gr +23 -25
  60. package/result.md +54 -39
  61. package/runtime/atof/common.gr +78 -82
  62. package/runtime/atof/common.md +22 -10
  63. package/runtime/atof/decimal.gr +102 -127
  64. package/runtime/atof/decimal.md +28 -7
  65. package/runtime/atof/lemire.gr +56 -71
  66. package/runtime/atof/lemire.md +9 -1
  67. package/runtime/atof/parse.gr +83 -110
  68. package/runtime/atof/parse.md +12 -2
  69. package/runtime/atof/slow.gr +28 -35
  70. package/runtime/atof/slow.md +9 -1
  71. package/runtime/atof/table.gr +19 -18
  72. package/runtime/atof/table.md +10 -2
  73. package/runtime/atoi/parse.gr +153 -136
  74. package/runtime/atoi/parse.md +50 -1
  75. package/runtime/bigint.gr +410 -517
  76. package/runtime/bigint.md +71 -57
  77. package/runtime/compare.gr +176 -85
  78. package/runtime/compare.md +31 -1
  79. package/runtime/dataStructures.gr +144 -32
  80. package/runtime/dataStructures.md +267 -31
  81. package/runtime/debugPrint.gr +34 -15
  82. package/runtime/debugPrint.md +37 -5
  83. package/runtime/equal.gr +53 -52
  84. package/runtime/equal.md +30 -1
  85. package/runtime/exception.gr +38 -47
  86. package/runtime/exception.md +10 -8
  87. package/runtime/gc.gr +23 -152
  88. package/runtime/gc.md +13 -17
  89. package/runtime/malloc.gr +31 -31
  90. package/runtime/malloc.md +11 -3
  91. package/runtime/numberUtils.gr +191 -172
  92. package/runtime/numberUtils.md +17 -9
  93. package/runtime/numbers.gr +1695 -1021
  94. package/runtime/numbers.md +1098 -134
  95. package/runtime/string.gr +540 -242
  96. package/runtime/string.md +76 -6
  97. package/runtime/unsafe/constants.gr +30 -13
  98. package/runtime/unsafe/constants.md +80 -0
  99. package/runtime/unsafe/conv.gr +55 -28
  100. package/runtime/unsafe/conv.md +41 -9
  101. package/runtime/unsafe/memory.gr +10 -30
  102. package/runtime/unsafe/memory.md +15 -19
  103. package/runtime/unsafe/tags.gr +37 -21
  104. package/runtime/unsafe/tags.md +88 -8
  105. package/runtime/unsafe/wasmf32.gr +30 -36
  106. package/runtime/unsafe/wasmf32.md +64 -56
  107. package/runtime/unsafe/wasmf64.gr +30 -36
  108. package/runtime/unsafe/wasmf64.md +64 -56
  109. package/runtime/unsafe/wasmi32.gr +49 -66
  110. package/runtime/unsafe/wasmi32.md +102 -94
  111. package/runtime/unsafe/wasmi64.gr +52 -79
  112. package/runtime/unsafe/wasmi64.md +108 -100
  113. package/runtime/utils/printing.gr +13 -15
  114. package/runtime/utils/printing.md +11 -3
  115. package/runtime/wasi.gr +294 -295
  116. package/runtime/wasi.md +62 -42
  117. package/set.gr +574 -64
  118. package/set.md +634 -54
  119. package/stack.gr +181 -64
  120. package/stack.md +271 -42
  121. package/string.gr +453 -533
  122. package/string.md +241 -151
  123. package/uint16.gr +369 -0
  124. package/uint16.md +585 -0
  125. package/uint32.gr +470 -0
  126. package/uint32.md +737 -0
  127. package/uint64.gr +471 -0
  128. package/uint64.md +737 -0
  129. package/uint8.gr +369 -0
  130. package/uint8.md +585 -0
  131. package/uri.gr +1093 -0
  132. package/uri.md +477 -0
  133. package/{sys → wasi}/file.gr +914 -500
  134. package/{sys → wasi}/file.md +454 -50
  135. package/wasi/process.gr +292 -0
  136. package/{sys → wasi}/process.md +164 -6
  137. package/wasi/random.gr +77 -0
  138. package/wasi/random.md +80 -0
  139. package/{sys → wasi}/time.gr +15 -22
  140. package/{sys → wasi}/time.md +5 -5
  141. package/immutablearray.gr +0 -929
  142. package/immutablearray.md +0 -1038
  143. package/immutablemap.gr +0 -493
  144. package/immutablemap.md +0 -479
  145. package/immutablepriorityqueue.gr +0 -360
  146. package/immutablepriorityqueue.md +0 -291
  147. package/immutableset.gr +0 -498
  148. package/immutableset.md +0 -449
  149. package/runtime/debug.gr +0 -2
  150. package/runtime/debug.md +0 -6
  151. package/runtime/unsafe/errors.gr +0 -36
  152. package/runtime/unsafe/errors.md +0 -204
  153. package/sys/process.gr +0 -254
  154. package/sys/random.gr +0 -79
  155. package/sys/random.md +0 -66
@@ -11,11 +11,11 @@
11
11
  * the Software, and to permit persons to whom the Software
12
12
  * is furnished to do so, subject to the following
13
13
  * conditions:
14
- *
14
+ *
15
15
  * The above copyright notice and this permission notice
16
16
  * shall be included in all copies or substantial portions
17
17
  * of the Software.
18
- *
18
+ *
19
19
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
20
20
  * ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
21
21
  * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
@@ -26,16 +26,19 @@
26
26
  * IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27
27
  * DEALINGS IN THE SOFTWARE.
28
28
  */
29
+ module Slow
29
30
 
30
31
  //! Slow, fallback algorithm for cases the Eisel-Lemire algorithm cannot round.
31
32
 
32
- import WasmI32 from "runtime/unsafe/wasmi32"
33
- import WasmI64 from "runtime/unsafe/wasmi64"
34
- import Memory from "runtime/unsafe/memory"
35
- import { newInt32, newInt64 } from "runtime/dataStructures"
33
+ from "runtime/unsafe/wasmi32" include WasmI32
34
+ from "runtime/unsafe/wasmi64" include WasmI64
35
+ from "runtime/unsafe/memory" include Memory
36
+ from "runtime/dataStructures" include DataStructures
37
+ use DataStructures.{ newInt32, newInt64 }
36
38
 
37
- import {
38
- BiasedFp,
39
+ from "runtime/atof/common" include Common
40
+ use Common.{
41
+ type BiasedFp,
39
42
  fpZero,
40
43
  fpInf,
41
44
  fpErr,
@@ -43,8 +46,9 @@ import {
43
46
  _INFINITE_POWER,
44
47
  _MANTISSA_EXPLICIT_BITS_32,
45
48
  _MANTISSA_EXPLICIT_BITS_64,
46
- } from "./common"
47
- import Decimal, { parseDecimal, _DECIMAL_POINT_RANGE } from "./decimal"
49
+ }
50
+ from "runtime/atof/decimal" include Decimal
51
+ use Decimal.{ parseDecimal, _DECIMAL_POINT_RANGE }
48
52
 
49
53
  @unsafe
50
54
  let _MAX_SHIFT = 60n
@@ -55,7 +59,7 @@ let mut _POWERS = -1n
55
59
 
56
60
  @unsafe
57
61
  let get_POWERS = () => {
58
- let (==) = WasmI32.eq
62
+ use WasmI32.{ (==) }
59
63
  if (_POWERS == -1n) {
60
64
  _POWERS = Memory.malloc(130n)
61
65
  WasmI32.store8(_POWERS, 0n, 0n)
@@ -83,7 +87,8 @@ let get_POWERS = () => {
83
87
 
84
88
  @unsafe
85
89
  let getShift = n => {
86
- if (WasmI32.ltS(n, _NUM_POWERS)) {
90
+ use WasmI32.{ (<) }
91
+ if (n < _NUM_POWERS) {
87
92
  WasmI32.load8U(get_POWERS(), n)
88
93
  } else {
89
94
  _MAX_SHIFT
@@ -112,22 +117,14 @@ let getShift = n => {
112
117
  * available here: <https://arxiv.org/pdf/2101.11408.pdf#section.11>.
113
118
  */
114
119
  @unsafe
115
- export let parseLongMantissa = (s: String) => {
116
- let (+) = WasmI32.add
117
- let (-) = WasmI32.sub
118
- let (&) = WasmI32.and
119
- let (<) = WasmI32.ltS
120
- let (>) = WasmI32.gtS
121
- let (>=) = WasmI32.geS
122
- let (<=) = WasmI32.leS
123
- let (>>) = WasmI32.shrU
124
- let (<<) = WasmI32.shl
125
- let (==) = WasmI32.eq
120
+ provide let parseLongMantissa = (s: String) => {
121
+ use WasmI32.{ (+), (-), (&), (<), (>), (>=), (<=), (==) }
122
+ use WasmI64.{ (+) as addWasmI64, (<<) }
126
123
 
127
124
  let mut d = parseDecimal(s)
128
125
  let digits = WasmI32.fromGrain(d.digits)
129
- let mut numDigits = WasmI32.load(WasmI32.fromGrain(d.numDigits), 8n)
130
- let mut decimalPoint = WasmI32.load(WasmI32.fromGrain(d.decimalPoint), 8n)
126
+ let mut numDigits = WasmI32.load(WasmI32.fromGrain(d.numDigits), 4n)
127
+ let mut decimalPoint = WasmI32.load(WasmI32.fromGrain(d.decimalPoint), 4n)
131
128
 
132
129
  // Short-circuit if the value can only be a literal 0 or infinity.
133
130
  if (numDigits == 0n || decimalPoint < -324n) {
@@ -142,7 +139,7 @@ export let parseLongMantissa = (s: String) => {
142
139
  let n = decimalPoint
143
140
  let shift = getShift(n)
144
141
  Decimal.rightShift(d, shift)
145
- decimalPoint = WasmI32.load(WasmI32.fromGrain(d.decimalPoint), 8n)
142
+ decimalPoint = WasmI32.load(WasmI32.fromGrain(d.decimalPoint), 4n)
146
143
  if (decimalPoint < 0n - _DECIMAL_POINT_RANGE) {
147
144
  done = true
148
145
  break
@@ -169,7 +166,7 @@ export let parseLongMantissa = (s: String) => {
169
166
  getShift(0n - decimalPoint)
170
167
  }
171
168
  Decimal.leftShift(d, shift)
172
- decimalPoint = WasmI32.load(WasmI32.fromGrain(d.decimalPoint), 8n)
169
+ decimalPoint = WasmI32.load(WasmI32.fromGrain(d.decimalPoint), 4n)
173
170
  if (decimalPoint > _DECIMAL_POINT_RANGE) {
174
171
  done = true
175
172
  break
@@ -200,7 +197,7 @@ export let parseLongMantissa = (s: String) => {
200
197
  if (
201
198
  WasmI64.geU(
202
199
  mantissa,
203
- WasmI64.shl(1N, WasmI64.add(_MANTISSA_EXPLICIT_BITS_64, 1N))
200
+ 1N << addWasmI64(_MANTISSA_EXPLICIT_BITS_64, 1N)
204
201
  )
205
202
  ) {
206
203
  // Rounding up overflowed to the carry bit, need to
@@ -216,16 +213,12 @@ export let parseLongMantissa = (s: String) => {
216
213
  fpInf()
217
214
  } else {
218
215
  let mut power2 = exp2 - _MINIMUM_EXPONENT
219
- if (
220
- WasmI64.ltU(mantissa, WasmI64.shl(1N, _MANTISSA_EXPLICIT_BITS_64))
221
- ) {
216
+ if (WasmI64.ltU(mantissa, 1N << _MANTISSA_EXPLICIT_BITS_64)) {
222
217
  power2 -= 1n
223
218
  }
219
+ use WasmI64.{ (-), (&) }
224
220
  // Zero out all the bits above the explicit mantissa bits.
225
- mantissa = WasmI64.and(
226
- mantissa,
227
- WasmI64.sub(WasmI64.shl(1N, _MANTISSA_EXPLICIT_BITS_64), 1N)
228
- )
221
+ mantissa = mantissa & (1N << _MANTISSA_EXPLICIT_BITS_64) - 1N
229
222
  {
230
223
  f: WasmI32.toGrain(newInt64(mantissa)): Int64,
231
224
  e: WasmI32.toGrain(newInt32(power2)): Int32,
@@ -1,6 +1,14 @@
1
+ ---
2
+ title: Slow
3
+ ---
4
+
5
+ ## Values
6
+
7
+ Functions and constants included in the Slow module.
8
+
1
9
  ### Slow.**parseLongMantissa**
2
10
 
3
11
  ```grain
4
- parseLongMantissa : String -> %Common.BiasedFp
12
+ parseLongMantissa : (s: String) => Common.BiasedFp
5
13
  ```
6
14
 
@@ -1,16 +1,17 @@
1
- /* grainc-flags --no-pervasives */
1
+ @noPervasives
2
+ module Table
2
3
 
3
- import WasmI32 from "runtime/unsafe/wasmi32"
4
- import WasmI64 from "runtime/unsafe/wasmi64"
5
- import WasmF64 from "runtime/unsafe/wasmf64"
6
- import Memory from "runtime/unsafe/memory"
4
+ from "runtime/unsafe/wasmi32" include WasmI32
5
+ from "runtime/unsafe/wasmi64" include WasmI64
6
+ from "runtime/unsafe/wasmf64" include WasmF64
7
+ from "runtime/unsafe/memory" include Memory
7
8
 
8
9
  @unsafe
9
10
  let mut _F64_POWERS10_FAST_PATH = -1n
10
11
 
11
12
  @unsafe
12
- export let get_F64_POWERS10_FAST_PATH = () => {
13
- let (==) = WasmI32.eq
13
+ provide let get_F64_POWERS10_FAST_PATH = () => {
14
+ use WasmI32.{ (==) }
14
15
  if (_F64_POWERS10_FAST_PATH == -1n) {
15
16
  _F64_POWERS10_FAST_PATH = Memory.malloc(256n) // 32 x 64-bit values
16
17
 
@@ -37,15 +38,15 @@ export let get_F64_POWERS10_FAST_PATH = () => {
37
38
  WasmF64.store(_F64_POWERS10_FAST_PATH, 1e20W, 160n)
38
39
  WasmF64.store(_F64_POWERS10_FAST_PATH, 1e21W, 168n)
39
40
  WasmF64.store(_F64_POWERS10_FAST_PATH, 1e22W, 176n)
40
- WasmF64.store(_F64_POWERS10_FAST_PATH, 0.W, 184n)
41
- WasmF64.store(_F64_POWERS10_FAST_PATH, 0.W, 192n)
42
- WasmF64.store(_F64_POWERS10_FAST_PATH, 0.W, 200n)
43
- WasmF64.store(_F64_POWERS10_FAST_PATH, 0.W, 208n)
44
- WasmF64.store(_F64_POWERS10_FAST_PATH, 0.W, 216n)
45
- WasmF64.store(_F64_POWERS10_FAST_PATH, 0.W, 224n)
46
- WasmF64.store(_F64_POWERS10_FAST_PATH, 0.W, 232n)
47
- WasmF64.store(_F64_POWERS10_FAST_PATH, 0.W, 240n)
48
- WasmF64.store(_F64_POWERS10_FAST_PATH, 0.W, 248n)
41
+ WasmF64.store(_F64_POWERS10_FAST_PATH, 0.0W, 184n)
42
+ WasmF64.store(_F64_POWERS10_FAST_PATH, 0.0W, 192n)
43
+ WasmF64.store(_F64_POWERS10_FAST_PATH, 0.0W, 200n)
44
+ WasmF64.store(_F64_POWERS10_FAST_PATH, 0.0W, 208n)
45
+ WasmF64.store(_F64_POWERS10_FAST_PATH, 0.0W, 216n)
46
+ WasmF64.store(_F64_POWERS10_FAST_PATH, 0.0W, 224n)
47
+ WasmF64.store(_F64_POWERS10_FAST_PATH, 0.0W, 232n)
48
+ WasmF64.store(_F64_POWERS10_FAST_PATH, 0.0W, 240n)
49
+ WasmF64.store(_F64_POWERS10_FAST_PATH, 0.0W, 248n)
49
50
  }
50
51
  _F64_POWERS10_FAST_PATH
51
52
  }
@@ -54,8 +55,8 @@ export let get_F64_POWERS10_FAST_PATH = () => {
54
55
  let mut _POWERS5 = -1n
55
56
 
56
57
  @unsafe
57
- export let get_POWERS5 = () => {
58
- let (==) = WasmI32.eq
58
+ provide let get_POWERS5 = () => {
59
+ use WasmI32.{ (==) }
59
60
  if (_POWERS5 == -1n) {
60
61
  _POWERS5 = Memory.malloc(10416n) // 1302 x 64-bit values
61
62
  //5^-342
@@ -1,12 +1,20 @@
1
+ ---
2
+ title: Table
3
+ ---
4
+
5
+ ## Values
6
+
7
+ Functions and constants included in the Table module.
8
+
1
9
  ### Table.**get_F64_POWERS10_FAST_PATH**
2
10
 
3
11
  ```grain
4
- get_F64_POWERS10_FAST_PATH : () -> WasmI32
12
+ get_F64_POWERS10_FAST_PATH : () => WasmI32
5
13
  ```
6
14
 
7
15
  ### Table.**get_POWERS5**
8
16
 
9
17
  ```grain
10
- get_POWERS5 : () -> WasmI32
18
+ get_POWERS5 : () => WasmI32
11
19
  ```
12
20
 
@@ -1,24 +1,51 @@
1
- // TODO(#1050): Remove dependency on Pervasives once Option/Result types are imbedded in the compiler
1
+ @noPervasives
2
+ module Parse
2
3
 
3
- import WasmI32, {
4
- add as (+),
5
- sub as (-),
4
+ from "runtime/unsafe/wasmi32" include WasmI32
5
+ use WasmI32.{
6
+ (+),
7
+ (-),
6
8
  gtU as (>),
7
9
  geU as (>=),
8
10
  ltU as (<),
9
- shrS as (>>),
10
- eq as (==),
11
- ne as (!=),
12
- and as (&),
13
- } from "runtime/unsafe/wasmi32"
14
- import WasmI64 from "runtime/unsafe/wasmi64"
15
- import Memory from "runtime/unsafe/memory"
16
- import Tags from "runtime/unsafe/tags"
17
- import BI from "runtime/bigint"
18
- import { reducedInteger } from "runtime/numbers"
11
+ (>>),
12
+ (==),
13
+ (!=),
14
+ (&),
15
+ }
16
+ from "runtime/unsafe/wasmi64" include WasmI64
17
+ from "runtime/unsafe/memory" include Memory
18
+ from "runtime/unsafe/tags" include Tags
19
+ from "runtime/bigint" include Bigint as BI
20
+ from "runtime/numbers" include Numbers
21
+ use Numbers.{ reducedInteger }
22
+
23
+ /**
24
+ * Represents an error that occurred trying to parse an integer.
25
+ *
26
+ * @since v0.6.0
27
+ */
28
+ provide enum ParseIntError {
29
+ /**
30
+ * Represents an error caused by trying to parse an empty string.
31
+ */
32
+ ParseIntEmptyString,
33
+ /**
34
+ * Represents an error caused by trying to parse a string with an invalid character.
35
+ */
36
+ ParseIntInvalidDigit,
37
+ /**
38
+ * Represents an error caused by trying to parse with an invalid radix.
39
+ */
40
+ ParseIntInvalidRadix,
41
+ }
42
+
43
+ primitive (&&) = "@and"
44
+ primitive (||) = "@or"
19
45
 
20
46
  @unsafe
21
- export let parseInt = (string: String, radix: Number) => {
47
+ provide let parseInt = (string: String, radix: Number) => {
48
+ use WasmI64.{ (+) as addWasmI64 }
22
49
  let _CHAR_0 = 0x30n
23
50
  let _CHAR_B = 0x42n
24
51
  let _CHAR_b = 0x62n
@@ -51,146 +78,136 @@ export let parseInt = (string: String, radix: Number) => {
51
78
  radix < WasmI32.fromGrain(2) ||
52
79
  radix > WasmI32.fromGrain(36)
53
80
  ) {
54
- Err("Radix must be an integer between 2 and 36")
55
- } else if (WasmI32.eqz(strLen)) {
56
- Err("Invalid input")
57
- } else {
58
- let mut char = WasmI32.load8U(offset, 0n)
59
-
60
- let mut limit = WasmI64.add(_INT_MIN, 1N)
61
-
62
- // Check for a sign
63
- let mut negative = false
64
- if (char == _CHAR_MINUS) {
65
- negative = true
66
- offset += 1n
67
- limit = _INT_MIN
68
- char = WasmI32.load8U(offset, 0n)
69
- }
81
+ return Err(ParseIntInvalidRadix)
82
+ }
70
83
 
71
- let mut radix = WasmI64.extendI32S(radix >> 1n)
72
-
73
- // Check if we should override the supplied radix
74
- if (char == _CHAR_0 && strLen > 2n) {
75
- match (WasmI32.load8U(offset, 1n)) {
76
- c when c == _CHAR_B || c == _CHAR_b => {
77
- radix = 2N
78
- offset += 2n
79
- },
80
- c when c == _CHAR_O || c == _CHAR_o => {
81
- radix = 8N
82
- offset += 2n
83
- },
84
- c when c == _CHAR_X || c == _CHAR_x => {
85
- radix = 16N
86
- offset += 2n
87
- },
88
- _ => void,
89
- }
90
- }
84
+ if (WasmI32.eqz(strLen)) {
85
+ return Err(ParseIntEmptyString)
86
+ }
91
87
 
92
- // We try to avoid allocating a BigInt if it's not needed
93
- let mut value = 0N
94
- let mut radixBigInt = 0n
95
- let mut valueBigInt = 0n
96
- let mut isBigInt = 0n
88
+ let mut char = WasmI32.load8U(offset, 0n)
97
89
 
98
- // we use 0 to represent no error and 1 to represent an invalid input
99
- let mut error = 1n
90
+ let mut limit = addWasmI64(_INT_MIN, 1N)
100
91
 
101
- for (let mut i = offset; i < strEnd; i += 1n) {
102
- let char = WasmI32.load8U(i, 0n)
92
+ // Check for a sign
93
+ let mut negative = false
94
+ if (char == _CHAR_MINUS) {
95
+ negative = true
96
+ offset += 1n
97
+ limit = _INT_MIN
98
+ char = WasmI32.load8U(offset, 0n)
99
+ }
103
100
 
104
- // Ignore underscore characters
105
- if (char == _CHAR_UNDERSCORE) {
106
- continue
107
- }
101
+ let mut radix = WasmI64.extendI32S(radix >> 1n)
108
102
 
109
- // We've seen at least one non-underscore character, so we'll consider
110
- // the input valid until we find out otherwise
103
+ // Check if we should override the supplied radix
104
+ if (char == _CHAR_0 && strLen > 2n) {
105
+ match (WasmI32.load8U(offset, 1n)) {
106
+ c when c == _CHAR_B || c == _CHAR_b => {
107
+ radix = 2N
108
+ offset += 2n
109
+ },
110
+ c when c == _CHAR_O || c == _CHAR_o => {
111
+ radix = 8N
112
+ offset += 2n
113
+ },
114
+ c when c == _CHAR_X || c == _CHAR_x => {
115
+ radix = 16N
116
+ offset += 2n
117
+ },
118
+ _ => void,
119
+ }
120
+ }
111
121
 
112
- error = 0n
122
+ // We try to avoid allocating a BigInt if it's not needed
123
+ let mut value = 0N
124
+ let mut radixBigInt = 0n
125
+ let mut valueBigInt = 0n
126
+ let mut isBigInt = 0n
127
+ let mut sawDigit = 0n
113
128
 
114
- let mut digit = 0n
129
+ for (let mut i = offset; i < strEnd; i += 1n) {
130
+ let char = WasmI32.load8U(i, 0n)
115
131
 
116
- match (char) {
117
- c when c - _CHAR_0 < 10n => digit = char - _CHAR_0,
118
- c when c - _CHAR_A < 26n => digit = char - _CHAR_A + 10n,
119
- c when c - _CHAR_a < 26n => digit = char - _CHAR_a + 10n,
120
- _ => {
121
- error = 1n
122
- // invalid digit
123
- break
124
- },
125
- }
132
+ // Ignore underscore characters
133
+ if (char == _CHAR_UNDERSCORE) {
134
+ continue
135
+ }
126
136
 
127
- if (digit >= WasmI32.wrapI64(radix)) {
128
- error = 1n
129
- // invalid digit
130
- break
131
- }
137
+ sawDigit = 1n
132
138
 
133
- let digit = WasmI64.extendI32U(digit)
134
-
135
- if (WasmI32.eqz(isBigInt)) {
136
- let prevValue = value
137
- value = WasmI64.mul(value, radix)
138
- // Check for overflow
139
- // 64-bit int min + 1
140
- if (WasmI64.ltS(value, WasmI64.add(limit, digit))) {
141
- // we overflowed. allocate BigInt and use instead
142
- isBigInt = 1n
143
- valueBigInt = BI.makeWrappedUint64(WasmI64.mul(prevValue, -1N))
144
- radixBigInt = BI.makeWrappedUint64(radix)
145
- let newvalue = BI.mul(valueBigInt, radixBigInt)
146
- Memory.decRef(valueBigInt)
147
- valueBigInt = newvalue
148
- let newvalue = BI.addInt(valueBigInt, digit)
149
- Memory.decRef(valueBigInt)
150
- valueBigInt = newvalue
151
- } else {
152
- // To quote the OpenJDK,
153
- // "Accumulating negatively avoids surprises near MAX_VALUE"
154
- // The minimum value of a 64-bit integer (-9223372036854775808) can't be
155
- // represented as a positive number because it would be larger than the
156
- // maximum 64-bit integer (9223372036854775807), so we'd be unable to
157
- // parse negatives as positives and multiply by the sign at the end.
158
- // Instead, we represent all positive numbers as negative numbers since
159
- // we have one unit more headroom.
160
- value = WasmI64.sub(value, digit)
161
- }
162
- } else {
139
+ let mut digit = 0n
140
+
141
+ match (char) {
142
+ c when c - _CHAR_0 < 10n => digit = char - _CHAR_0,
143
+ c when c - _CHAR_A < 26n => digit = char - _CHAR_A + 10n,
144
+ c when c - _CHAR_a < 26n => digit = char - _CHAR_a + 10n,
145
+ _ => {
146
+ return Err(ParseIntInvalidDigit)
147
+ },
148
+ }
149
+
150
+ if (digit >= WasmI32.wrapI64(radix)) {
151
+ return Err(ParseIntInvalidDigit)
152
+ }
153
+
154
+ let digit = WasmI64.extendI32U(digit)
155
+
156
+ if (WasmI32.eqz(isBigInt)) {
157
+ use WasmI64.{ (+) }
158
+ use WasmI64.{ (*), (<) }
159
+ let prevValue = value
160
+ value *= radix
161
+ // Check for overflow
162
+ // 64-bit int min + 1
163
+ if (value < limit + digit) {
164
+ // we overflowed. allocate BigInt and use instead
165
+ isBigInt = 1n
166
+ valueBigInt = BI.makeWrappedUint64(prevValue * -1N)
167
+ radixBigInt = BI.makeWrappedUint64(radix)
163
168
  let newvalue = BI.mul(valueBigInt, radixBigInt)
164
169
  Memory.decRef(valueBigInt)
165
170
  valueBigInt = newvalue
166
171
  let newvalue = BI.addInt(valueBigInt, digit)
167
172
  Memory.decRef(valueBigInt)
168
173
  valueBigInt = newvalue
174
+ } else {
175
+ use WasmI64.{ (-) }
176
+ // To quote the OpenJDK,
177
+ // "Accumulating negatively avoids surprises near MAX_VALUE"
178
+ // The minimum value of a 64-bit integer (-9223372036854775808) can't be
179
+ // represented as a positive number because it would be larger than the
180
+ // maximum 64-bit integer (9223372036854775807), so we'd be unable to
181
+ // parse negatives as positives and multiply by the sign at the end.
182
+ // Instead, we represent all positive numbers as negative numbers since
183
+ // we have one unit more headroom.
184
+ value -= digit
169
185
  }
186
+ } else {
187
+ let newvalue = BI.mul(valueBigInt, radixBigInt)
188
+ Memory.decRef(valueBigInt)
189
+ valueBigInt = newvalue
190
+ let newvalue = BI.addInt(valueBigInt, digit)
191
+ Memory.decRef(valueBigInt)
192
+ valueBigInt = newvalue
170
193
  }
194
+ }
195
+ use WasmI64.{ (*) }
196
+ // TODO: Verify this is suitable for handling "_"
197
+ if (WasmI32.eqz(sawDigit)) return Err(ParseIntInvalidDigit)
198
+
199
+ if (WasmI32.eqz(isBigInt)) {
200
+ let value = if (negative) value else value * -1N
201
+ let number = WasmI32.toGrain(Memory.incRef(reducedInteger(value))): Number
202
+ return Ok(number)
203
+ }
171
204
 
172
- match (error) {
173
- 1n => {
174
- Err("Invalid digit in input")
175
- },
176
- _ => {
177
- if (WasmI32.eqz(isBigInt)) {
178
- let value = if (negative) value else WasmI64.mul(value, -1N)
179
- let number = WasmI32.toGrain(
180
- Memory.incRef(reducedInteger(value))
181
- ): Number
182
- Ok(number)
183
- } else {
184
- // BigInt number is accumulated in positive form
185
- if (negative) {
186
- let newvalue = BI.negate(valueBigInt)
187
- Memory.decRef(valueBigInt)
188
- Ok(WasmI32.toGrain(newvalue))
189
- } else {
190
- Ok(WasmI32.toGrain(valueBigInt))
191
- }
192
- }
193
- },
194
- }
205
+ // BigInt number is accumulated in positive form
206
+ if (negative) {
207
+ let newvalue = BI.negate(valueBigInt)
208
+ Memory.decRef(valueBigInt)
209
+ return Ok(WasmI32.toGrain(newvalue))
195
210
  }
211
+
212
+ return Ok(WasmI32.toGrain(valueBigInt))
196
213
  }
@@ -1,6 +1,55 @@
1
+ ---
2
+ title: Parse
3
+ ---
4
+
5
+ ## Types
6
+
7
+ Type declarations included in the Parse module.
8
+
9
+ ### Parse.**ParseIntError**
10
+
11
+ <details disabled>
12
+ <summary tabindex="-1">Added in <code>0.6.0</code></summary>
13
+ No other changes yet.
14
+ </details>
15
+
16
+ ```grain
17
+ enum ParseIntError {
18
+ ParseIntEmptyString,
19
+ ParseIntInvalidDigit,
20
+ ParseIntInvalidRadix,
21
+ }
22
+ ```
23
+
24
+ Represents an error that occurred trying to parse an integer.
25
+
26
+ Variants:
27
+
28
+ ```grain
29
+ ParseIntEmptyString
30
+ ```
31
+
32
+ Represents an error caused by trying to parse an empty string.
33
+
34
+ ```grain
35
+ ParseIntInvalidDigit
36
+ ```
37
+
38
+ Represents an error caused by trying to parse a string with an invalid character.
39
+
40
+ ```grain
41
+ ParseIntInvalidRadix
42
+ ```
43
+
44
+ Represents an error caused by trying to parse with an invalid radix.
45
+
46
+ ## Values
47
+
48
+ Functions and constants included in the Parse module.
49
+
1
50
  ### Parse.**parseInt**
2
51
 
3
52
  ```grain
4
- parseInt : (String, Number) -> Result<Number, String>
53
+ parseInt : (string: String, radix: Number) => Result<Number, ParseIntError>
5
54
  ```
6
55