@grain/stdlib 0.4.0 → 0.4.4
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 +65 -0
- package/LICENSE +21 -0
- package/README.md +34 -0
- package/array.gr +136 -44
- package/array.md +97 -21
- package/buffer.gr +495 -424
- package/buffer.md +850 -0
- package/bytes.gr +512 -407
- package/bytes.md +621 -0
- package/char.gr +11 -3
- package/hash.gr +26 -3
- package/hash.md +44 -0
- package/list.gr +54 -0
- package/number.gr +24 -6
- package/number.md +49 -17
- package/option.gr +244 -37
- package/option.md +579 -0
- package/package.json +33 -29
- package/queue.gr +98 -29
- package/queue.md +191 -0
- package/range.md +1 -1
- package/regex.gr +3055 -0
- package/regex.md +449 -0
- package/result.gr +216 -70
- package/result.md +446 -0
- package/runtime/gc.gr +2 -2
- package/runtime/string.gr +56 -24
- package/runtime/stringUtils.gr +172 -0
- package/runtime/unsafe/conv.gr +43 -0
- package/set.gr +172 -5
- package/set.md +502 -0
- package/stack.md +143 -0
- package/string.gr +444 -230
- package/string.md +815 -0
- package/sys/file.gr +3 -2
- package/sys/file.md +2 -2
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
// TODO(#1050): Remove dependency on Pervasives once Option/Result types are imbedded in the compiler
|
|
2
|
+
|
|
3
|
+
import WasmI32, {
|
|
4
|
+
add as (+),
|
|
5
|
+
sub as (-),
|
|
6
|
+
gtU as (>),
|
|
7
|
+
geU as (>=),
|
|
8
|
+
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 { reducedInteger } from "runtime/numbers"
|
|
18
|
+
|
|
19
|
+
@disableGC
|
|
20
|
+
export let rec parseInt = (string: String, radix: Number) => {
|
|
21
|
+
let _CHAR_0 = 0x30n
|
|
22
|
+
let _CHAR_B = 0x42n
|
|
23
|
+
let _CHAR_b = 0x62n
|
|
24
|
+
let _CHAR_O = 0x4fn
|
|
25
|
+
let _CHAR_o = 0x6fn
|
|
26
|
+
let _CHAR_X = 0x58n
|
|
27
|
+
let _CHAR_x = 0x78n
|
|
28
|
+
|
|
29
|
+
let _CHAR_A = 0x41n
|
|
30
|
+
let _CHAR_a = 0x61n
|
|
31
|
+
|
|
32
|
+
let _CHAR_UNDERSCORE = 0x5fn
|
|
33
|
+
let _CHAR_MINUS = 0x2dn
|
|
34
|
+
|
|
35
|
+
let _INT_MIN = -9223372036854775808N
|
|
36
|
+
|
|
37
|
+
// Don't need to process Unicode length since if the string
|
|
38
|
+
// contains non-ascii characters, it's not a valid integer
|
|
39
|
+
let strLen = WasmI32.load(WasmI32.fromGrain(string), 4n)
|
|
40
|
+
|
|
41
|
+
// Our pointer within the string we're parsing, offset by the
|
|
42
|
+
// header
|
|
43
|
+
let mut offset = WasmI32.fromGrain(string) + 8n
|
|
44
|
+
|
|
45
|
+
let strEnd = offset + strLen
|
|
46
|
+
|
|
47
|
+
let radix = WasmI32.fromGrain(radix)
|
|
48
|
+
let result = if (WasmI32.eqz(radix & Tags._GRAIN_NUMBER_TAG_MASK) || radix < WasmI32.fromGrain(2) || radix > WasmI32.fromGrain(36)) {
|
|
49
|
+
Memory.incRef(WasmI32.fromGrain(Err))
|
|
50
|
+
Err("Radix must be an integer between 2 and 36")
|
|
51
|
+
} else if (WasmI32.eqz(strLen)) {
|
|
52
|
+
Memory.incRef(WasmI32.fromGrain(Err))
|
|
53
|
+
Err("Invalid input")
|
|
54
|
+
} else {
|
|
55
|
+
let mut char = WasmI32.load8U(offset, 0n)
|
|
56
|
+
|
|
57
|
+
let mut limit = WasmI64.add(_INT_MIN, 1N)
|
|
58
|
+
|
|
59
|
+
// Check for a sign
|
|
60
|
+
let mut negative = false
|
|
61
|
+
if (char == _CHAR_MINUS) {
|
|
62
|
+
negative = true
|
|
63
|
+
offset += 1n
|
|
64
|
+
limit = _INT_MIN
|
|
65
|
+
char = WasmI32.load8U(offset, 0n)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
let mut radix = WasmI64.extendI32S(radix >> 1n)
|
|
69
|
+
|
|
70
|
+
// Check if we should override the supplied radix
|
|
71
|
+
if (char == _CHAR_0 && strLen > 2n) {
|
|
72
|
+
match (WasmI32.load8U(offset, 1n)) {
|
|
73
|
+
c when c == _CHAR_B || c == _CHAR_b => {
|
|
74
|
+
radix = 2N
|
|
75
|
+
offset += 2n
|
|
76
|
+
},
|
|
77
|
+
c when c == _CHAR_O || c == _CHAR_o => {
|
|
78
|
+
radix = 8N
|
|
79
|
+
offset += 2n
|
|
80
|
+
},
|
|
81
|
+
c when c == _CHAR_X || c == _CHAR_x => {
|
|
82
|
+
radix = 16N
|
|
83
|
+
offset += 2n
|
|
84
|
+
},
|
|
85
|
+
_ => void,
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
let mut value = 0N
|
|
90
|
+
|
|
91
|
+
// we use 0 to represent no error, 1 to represent an invalid
|
|
92
|
+
// input, and 2 to represent an overflow
|
|
93
|
+
let mut error = 1n
|
|
94
|
+
|
|
95
|
+
for (let mut i = offset; i < strEnd; i += 1n) {
|
|
96
|
+
let char = WasmI32.load8U(i, 0n)
|
|
97
|
+
|
|
98
|
+
// Ignore underscore characters
|
|
99
|
+
if (char == _CHAR_UNDERSCORE) {
|
|
100
|
+
continue
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// We've seen at least one non-underscore character, so we'll consider
|
|
104
|
+
// the input valid until we find out otherwise
|
|
105
|
+
|
|
106
|
+
error = 0n
|
|
107
|
+
|
|
108
|
+
let mut digit = 0n
|
|
109
|
+
|
|
110
|
+
match (char) {
|
|
111
|
+
c when c - _CHAR_0 < 10n => digit = char - _CHAR_0,
|
|
112
|
+
c when c - _CHAR_A < 26n => digit = char - _CHAR_A + 10n,
|
|
113
|
+
c when c - _CHAR_a < 26n => digit = char - _CHAR_a + 10n,
|
|
114
|
+
_ => {
|
|
115
|
+
error = 1n
|
|
116
|
+
// invalid digit
|
|
117
|
+
break
|
|
118
|
+
},
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (digit >= WasmI32.wrapI64(radix)) {
|
|
122
|
+
error = 1n
|
|
123
|
+
// invalid digit
|
|
124
|
+
break
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
let digit = WasmI64.extendI32U(digit)
|
|
128
|
+
|
|
129
|
+
value = WasmI64.mul(value, radix)
|
|
130
|
+
|
|
131
|
+
// Check for overflow
|
|
132
|
+
// 64-bit int min + 1
|
|
133
|
+
if (WasmI64.ltS(value, WasmI64.add(limit, digit))) {
|
|
134
|
+
error = 2n
|
|
135
|
+
// overflow
|
|
136
|
+
break
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// To quote the OpenJDK,
|
|
140
|
+
// "Accumulating negatively avoids surprises near MAX_VALUE"
|
|
141
|
+
// The minimum value of a 64-bit integer (-9223372036854775808) can't be
|
|
142
|
+
// represented as a positive number because it would be larger than the
|
|
143
|
+
// maximum 64-bit integer (9223372036854775807), so we'd be unable to
|
|
144
|
+
// parse negatives as positives and multiply by the sign at the end.
|
|
145
|
+
// Instead, we represent all positive numbers as negative numbers since
|
|
146
|
+
// we have one unit more headroom.
|
|
147
|
+
value = WasmI64.sub(value, digit)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
match (error) {
|
|
151
|
+
1n => {
|
|
152
|
+
Memory.incRef(WasmI32.fromGrain(Err))
|
|
153
|
+
Err("Invalid digit in input")
|
|
154
|
+
},
|
|
155
|
+
2n => {
|
|
156
|
+
Memory.incRef(WasmI32.fromGrain(Err))
|
|
157
|
+
Err("Input out of range of representable integers")
|
|
158
|
+
},
|
|
159
|
+
_ => {
|
|
160
|
+
let value = if (negative) value else WasmI64.mul(value, -1N)
|
|
161
|
+
let number = WasmI32.toGrain(reducedInteger(value)): (Number)
|
|
162
|
+
Memory.incRef(WasmI32.fromGrain(Ok))
|
|
163
|
+
Ok(number)
|
|
164
|
+
},
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
Memory.decRef(WasmI32.fromGrain(parseInt))
|
|
169
|
+
Memory.decRef(radix)
|
|
170
|
+
|
|
171
|
+
result
|
|
172
|
+
}
|
package/runtime/unsafe/conv.gr
CHANGED
|
@@ -68,3 +68,46 @@ export let fromFloat64 = (n: Float64) => {
|
|
|
68
68
|
let ptr = WasmI32.fromGrain(n)
|
|
69
69
|
WasmF64.load(ptr, 8n)
|
|
70
70
|
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Converts a WasmI32 value to Number.
|
|
74
|
+
*
|
|
75
|
+
* This function is meant to be called from a `@disableGC` context without
|
|
76
|
+
* need to call incRef on the function.
|
|
77
|
+
*
|
|
78
|
+
* @param n: The WasmI32 to convert
|
|
79
|
+
* @returns The value converted to either a simple or a 32 bit heap allocated number.
|
|
80
|
+
*/
|
|
81
|
+
@disableGC
|
|
82
|
+
export let wasmI32ToNumber = (n: WasmI32) => {
|
|
83
|
+
// Follows a little optimization. Instead of testing if n is range of allowed
|
|
84
|
+
// non heap allocated simple numbers (-1073741824n..1073741823n), actually
|
|
85
|
+
// make a simple number, convert it back to WasmI32, and test if it stayed
|
|
86
|
+
// the same. This may sound counter intuitive, but for numbers that fit into
|
|
87
|
+
// the range it is faster than performing the check with WasmI32.geS and
|
|
88
|
+
// WasmI32.leS by about 20%. An additional ~33% is gained by avoiding a
|
|
89
|
+
// call to tagSimpleNumber and instead doing it inline. For the case of
|
|
90
|
+
// numbers not fitting into the range, the cost of these simple ALU
|
|
91
|
+
// instructions are negligible compared to heap allocations.
|
|
92
|
+
|
|
93
|
+
// First step: naively convert to simple number, just like tagSimpleNumber.
|
|
94
|
+
// This will overflow for values not in the range, but it's fine.
|
|
95
|
+
let simple = WasmI32.xor(WasmI32.shl(n, 1n), 1n)
|
|
96
|
+
|
|
97
|
+
// Untag it, just like numbers.untagSimple.
|
|
98
|
+
let untagged = WasmI32.shrS(simple, 1n)
|
|
99
|
+
|
|
100
|
+
let result = if (WasmI32.eq(untagged, n)) {
|
|
101
|
+
// Just test if the untagged number is the same. If it didn't overflow, then
|
|
102
|
+
// we're good. We just need to cast the raw value into a Number at the type
|
|
103
|
+
// system level.
|
|
104
|
+
WasmI32.toGrain(simple): (Number)
|
|
105
|
+
} else {
|
|
106
|
+
// If it did overflow, then the value differs and we need to discard it and
|
|
107
|
+
// allocate the number on the heap. A boxed 32 bit number actually is the
|
|
108
|
+
// same thing as an Int32. It only needs to be cast into Number.
|
|
109
|
+
let asInt32 = toInt32(n)
|
|
110
|
+
WasmI32.toGrain(WasmI32.fromGrain(asInt32)): (Number)
|
|
111
|
+
}
|
|
112
|
+
result
|
|
113
|
+
}
|
package/set.gr
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @module Set: A Set is an unordered collection of unique values. Operations on a Set mutate the internal state, so it never needs to be re-assigned.
|
|
3
|
+
* @example import Set from "set"
|
|
4
|
+
*
|
|
5
|
+
* @since 0.3.0
|
|
6
|
+
*/
|
|
3
7
|
import List from "list"
|
|
4
8
|
import Array from "array"
|
|
5
9
|
import { hash } from "hash"
|
|
@@ -15,14 +19,28 @@ record Set<k> {
|
|
|
15
19
|
}
|
|
16
20
|
|
|
17
21
|
// TODO: This could take an `eq` function to custom comparisons
|
|
18
|
-
|
|
19
|
-
|
|
22
|
+
/**
|
|
23
|
+
* Creates a new empty set with an initial storage of the given length. As values are added or removed, the length may grow or shrink. Generally, you won't need to care about the length of your set and can use `Set.make()` instead.
|
|
24
|
+
*
|
|
25
|
+
* @param storageLength: The initial storage length of the set
|
|
26
|
+
* @returns An empty set with the given initial storage length
|
|
27
|
+
*
|
|
28
|
+
* @since 0.3.0
|
|
29
|
+
*/
|
|
30
|
+
export let makeSized = (storageLength) => {
|
|
31
|
+
let buckets = Array.make(storageLength, None);
|
|
20
32
|
{
|
|
21
33
|
size: 0,
|
|
22
34
|
buckets
|
|
23
35
|
}
|
|
24
36
|
}
|
|
25
|
-
|
|
37
|
+
/**
|
|
38
|
+
* Creates a new, empty set.
|
|
39
|
+
*
|
|
40
|
+
* @returns An empty set
|
|
41
|
+
*
|
|
42
|
+
* @since 0.3.0
|
|
43
|
+
*/
|
|
26
44
|
export let make = () => {
|
|
27
45
|
makeSized(16)
|
|
28
46
|
}
|
|
@@ -92,6 +110,14 @@ let rec nodeInBucket = (key, node) => {
|
|
|
92
110
|
}
|
|
93
111
|
}
|
|
94
112
|
|
|
113
|
+
/**
|
|
114
|
+
* Adds a new value to the set. If the value already exists, nothing happens.
|
|
115
|
+
*
|
|
116
|
+
* @param key: The value to add
|
|
117
|
+
* @param set: The set to update
|
|
118
|
+
*
|
|
119
|
+
* @since 0.3.0
|
|
120
|
+
*/
|
|
95
121
|
export let add = (key, set) => {
|
|
96
122
|
let buckets = set.buckets;
|
|
97
123
|
let idx = getBucketIndex(key, buckets)
|
|
@@ -116,6 +142,15 @@ export let add = (key, set) => {
|
|
|
116
142
|
}
|
|
117
143
|
}
|
|
118
144
|
|
|
145
|
+
/**
|
|
146
|
+
* Determines if the set contains the given value.
|
|
147
|
+
*
|
|
148
|
+
* @param key: The value to search for
|
|
149
|
+
* @param set: The set to search
|
|
150
|
+
* @returns `true` if the set contains the given value or `false` otherwise
|
|
151
|
+
*
|
|
152
|
+
* @since 0.3.0
|
|
153
|
+
*/
|
|
119
154
|
export let contains = (key, set) => {
|
|
120
155
|
let buckets = set.buckets;
|
|
121
156
|
let idx = getBucketIndex(key, buckets);
|
|
@@ -140,6 +175,14 @@ let rec removeInBucket = (key, node) => {
|
|
|
140
175
|
}
|
|
141
176
|
}
|
|
142
177
|
|
|
178
|
+
/**
|
|
179
|
+
* Removes the given value from the set. If the value doesn't exist, nothing happens.
|
|
180
|
+
*
|
|
181
|
+
* @param key: The value to remove
|
|
182
|
+
* @param set: The set to update
|
|
183
|
+
*
|
|
184
|
+
* @since 0.3.0
|
|
185
|
+
*/
|
|
143
186
|
export let remove = (key, set) => {
|
|
144
187
|
let buckets = set.buckets;
|
|
145
188
|
let idx = getBucketIndex(key, buckets);
|
|
@@ -160,14 +203,37 @@ export let remove = (key, set) => {
|
|
|
160
203
|
}
|
|
161
204
|
}
|
|
162
205
|
|
|
206
|
+
/**
|
|
207
|
+
* Returns the number of values within the set.
|
|
208
|
+
*
|
|
209
|
+
* @param set: The set to inspect
|
|
210
|
+
* @returns The number of elements in the set
|
|
211
|
+
*
|
|
212
|
+
* @since 0.3.0
|
|
213
|
+
*/
|
|
163
214
|
export let size = (set) => {
|
|
164
215
|
set.size
|
|
165
216
|
}
|
|
166
217
|
|
|
218
|
+
/**
|
|
219
|
+
* Determines if the set contains no elements.
|
|
220
|
+
*
|
|
221
|
+
* @param set: The set to inspect
|
|
222
|
+
* @returns `true` if the given set is empty or `false` otherwise
|
|
223
|
+
*
|
|
224
|
+
* @since 0.3.0
|
|
225
|
+
*/
|
|
167
226
|
export let isEmpty = (set) => {
|
|
168
227
|
size(set) == 0
|
|
169
228
|
}
|
|
170
229
|
|
|
230
|
+
/**
|
|
231
|
+
* Resets the set by removing all values.
|
|
232
|
+
*
|
|
233
|
+
* @param set: The set to reset
|
|
234
|
+
*
|
|
235
|
+
* @since 0.3.0
|
|
236
|
+
*/
|
|
171
237
|
export let clear = (set) => {
|
|
172
238
|
set.size = 0;
|
|
173
239
|
let buckets = set.buckets;
|
|
@@ -186,6 +252,14 @@ let rec forEachBucket = (fn, node) => {
|
|
|
186
252
|
}
|
|
187
253
|
}
|
|
188
254
|
|
|
255
|
+
/**
|
|
256
|
+
* Iterates the set, calling an iterator function on each element.
|
|
257
|
+
*
|
|
258
|
+
* @param fn: The iterator function to call with each element
|
|
259
|
+
* @param set: The set to iterate
|
|
260
|
+
*
|
|
261
|
+
* @since 0.3.0
|
|
262
|
+
*/
|
|
189
263
|
export let forEach = (fn, set) => {
|
|
190
264
|
let buckets = set.buckets;
|
|
191
265
|
Array.forEach((bucket) => {
|
|
@@ -201,6 +275,16 @@ let rec reduceEachBucket = (fn, node, acc) => {
|
|
|
201
275
|
}
|
|
202
276
|
}
|
|
203
277
|
|
|
278
|
+
/**
|
|
279
|
+
* Combines all elements of a set using a reducer function.
|
|
280
|
+
*
|
|
281
|
+
* @param fn: The reducer function to call on each element, where the value returned will be the next accumulator value
|
|
282
|
+
* @param init: The initial value to use for the accumulator on the first iteration
|
|
283
|
+
* @param set: The set to iterate
|
|
284
|
+
* @returns The final accumulator returned from `fn`
|
|
285
|
+
*
|
|
286
|
+
* @since 0.3.0
|
|
287
|
+
*/
|
|
204
288
|
export let reduce = (fn, init, set) => {
|
|
205
289
|
let buckets = set.buckets;
|
|
206
290
|
let mut acc = init;
|
|
@@ -210,6 +294,14 @@ export let reduce = (fn, init, set) => {
|
|
|
210
294
|
acc
|
|
211
295
|
}
|
|
212
296
|
|
|
297
|
+
/**
|
|
298
|
+
* Removes elements from a set where a predicate function returns `false`.
|
|
299
|
+
*
|
|
300
|
+
* @param fn: The predicate function to indicate which elements to remove from the set, where returning `false` indicates the value should be removed
|
|
301
|
+
* @param set: The set to iterate
|
|
302
|
+
*
|
|
303
|
+
* @since 0.3.0
|
|
304
|
+
*/
|
|
213
305
|
export let filter = (predicate, set) => {
|
|
214
306
|
let keysToRemove = reduce((list, key) =>
|
|
215
307
|
if (!predicate(key)) {
|
|
@@ -222,14 +314,38 @@ export let filter = (predicate, set) => {
|
|
|
222
314
|
}, keysToRemove);
|
|
223
315
|
}
|
|
224
316
|
|
|
317
|
+
/**
|
|
318
|
+
* Removes elements from a set where a predicate function returns `true`.
|
|
319
|
+
*
|
|
320
|
+
* @param fn: The predicate function to indicate which elements to remove from the set, where returning `true` indicates the value should be removed
|
|
321
|
+
* @param set: The set to iterate
|
|
322
|
+
*
|
|
323
|
+
* @since 0.3.0
|
|
324
|
+
*/
|
|
225
325
|
export let reject = (predicate, set) => {
|
|
226
326
|
filter((key) => !predicate(key), set)
|
|
227
327
|
}
|
|
228
328
|
|
|
329
|
+
/**
|
|
330
|
+
* Converts a set into a list of its elements.
|
|
331
|
+
*
|
|
332
|
+
* @param set: The set to convert
|
|
333
|
+
* @returns A list containing all set values
|
|
334
|
+
*
|
|
335
|
+
* @since 0.3.0
|
|
336
|
+
*/
|
|
229
337
|
export let toList = (set) => {
|
|
230
338
|
reduce((list, key) => [key, ...list], [], set)
|
|
231
339
|
}
|
|
232
340
|
|
|
341
|
+
/**
|
|
342
|
+
* Creates a set from a list.
|
|
343
|
+
*
|
|
344
|
+
* @param list: The list to convert
|
|
345
|
+
* @returns A set containing all list values
|
|
346
|
+
*
|
|
347
|
+
* @since 0.3.0
|
|
348
|
+
*/
|
|
233
349
|
export let fromList = (list) => {
|
|
234
350
|
let set = make();
|
|
235
351
|
List.forEach((key) => {
|
|
@@ -238,10 +354,26 @@ export let fromList = (list) => {
|
|
|
238
354
|
set
|
|
239
355
|
}
|
|
240
356
|
|
|
357
|
+
/**
|
|
358
|
+
* Converts a set into an array of its elements.
|
|
359
|
+
*
|
|
360
|
+
* @param set: The set to convert
|
|
361
|
+
* @returns An array containing all set values
|
|
362
|
+
*
|
|
363
|
+
* @since 0.3.0
|
|
364
|
+
*/
|
|
241
365
|
export let toArray = (set) => {
|
|
242
366
|
Array.fromList(toList(set))
|
|
243
367
|
}
|
|
244
368
|
|
|
369
|
+
/**
|
|
370
|
+
* Creates a set from an array.
|
|
371
|
+
*
|
|
372
|
+
* @param array: The array to convert
|
|
373
|
+
* @returns A set containing all array values
|
|
374
|
+
*
|
|
375
|
+
* @since 0.3.0
|
|
376
|
+
*/
|
|
245
377
|
export let fromArray = (array) => {
|
|
246
378
|
let set = make();
|
|
247
379
|
Array.forEach((key) => {
|
|
@@ -250,6 +382,15 @@ export let fromArray = (array) => {
|
|
|
250
382
|
set
|
|
251
383
|
}
|
|
252
384
|
|
|
385
|
+
/**
|
|
386
|
+
* Combines two sets into a single set containing all elements from both sets.
|
|
387
|
+
*
|
|
388
|
+
* @param set1: The first set to combine
|
|
389
|
+
* @param set2: The second set to combine
|
|
390
|
+
* @returns A set containing all elements of both sets
|
|
391
|
+
*
|
|
392
|
+
* @since 0.3.0
|
|
393
|
+
*/
|
|
253
394
|
export let union = (set1, set2) => {
|
|
254
395
|
let set = make();
|
|
255
396
|
forEach((key) => {
|
|
@@ -261,6 +402,15 @@ export let union = (set1, set2) => {
|
|
|
261
402
|
set
|
|
262
403
|
}
|
|
263
404
|
|
|
405
|
+
/**
|
|
406
|
+
* Combines two sets into a single set containing only the elements not shared between both sets.
|
|
407
|
+
*
|
|
408
|
+
* @param set1: The first set to combine
|
|
409
|
+
* @param set2: The second set to combine
|
|
410
|
+
* @returns A set containing only unshared elements from both sets
|
|
411
|
+
*
|
|
412
|
+
* @since 0.3.0
|
|
413
|
+
*/
|
|
264
414
|
export let diff = (set1, set2) => {
|
|
265
415
|
let set = make();
|
|
266
416
|
forEach((key) => {
|
|
@@ -276,6 +426,15 @@ export let diff = (set1, set2) => {
|
|
|
276
426
|
set
|
|
277
427
|
}
|
|
278
428
|
|
|
429
|
+
/**
|
|
430
|
+
* Combines two sets into a single set containing only the elements shared between both sets.
|
|
431
|
+
*
|
|
432
|
+
* @param set1: The first set to combine
|
|
433
|
+
* @param set2: The second set to combine
|
|
434
|
+
* @returns A set containing only shared elements from both sets
|
|
435
|
+
*
|
|
436
|
+
* @since 0.3.0
|
|
437
|
+
*/
|
|
279
438
|
export let intersect = (set1, set2) => {
|
|
280
439
|
let set = make();
|
|
281
440
|
forEach((key) => {
|
|
@@ -293,6 +452,14 @@ export let intersect = (set1, set2) => {
|
|
|
293
452
|
|
|
294
453
|
// TODO: Should return a Record type instead of a Tuple
|
|
295
454
|
// Waiting on https://github.com/grain-lang/grain/issues/190
|
|
455
|
+
/**
|
|
456
|
+
* Provides data representing the internal state state of the set.
|
|
457
|
+
*
|
|
458
|
+
* @param set: The set to inspect
|
|
459
|
+
* @returns The internal state of the set
|
|
460
|
+
*
|
|
461
|
+
* @since 0.3.0
|
|
462
|
+
*/
|
|
296
463
|
export let getInternalStats = (set) => {
|
|
297
464
|
(set.size, Array.length(set.buckets))
|
|
298
465
|
}
|