@grain/stdlib 0.6.6 → 0.7.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 (137) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/LICENSE +1 -1
  3. package/README.md +2 -2
  4. package/array.gr +55 -7
  5. package/array.md +123 -77
  6. package/bigint.md +30 -30
  7. package/buffer.gr +20 -53
  8. package/buffer.md +47 -47
  9. package/bytes.gr +111 -35
  10. package/bytes.md +111 -32
  11. package/char.gr +201 -99
  12. package/char.md +361 -34
  13. package/exception.gr +11 -11
  14. package/exception.md +26 -1
  15. package/float32.gr +327 -3
  16. package/float32.md +606 -19
  17. package/float64.gr +320 -3
  18. package/float64.md +606 -19
  19. package/fs.gr +1082 -0
  20. package/fs.md +630 -0
  21. package/hash.gr +142 -88
  22. package/hash.md +102 -14
  23. package/int16.md +23 -23
  24. package/int32.gr +25 -4
  25. package/int32.md +65 -30
  26. package/int64.gr +26 -1
  27. package/int64.md +65 -30
  28. package/int8.md +23 -23
  29. package/json.gr +366 -51
  30. package/json.md +418 -2
  31. package/list.gr +328 -31
  32. package/list.md +492 -69
  33. package/map.gr +20 -12
  34. package/map.md +44 -38
  35. package/marshal.gr +41 -40
  36. package/marshal.md +2 -2
  37. package/number.gr +159 -30
  38. package/number.md +215 -38
  39. package/option.md +21 -21
  40. package/package.json +5 -3
  41. package/path.gr +48 -0
  42. package/path.md +103 -12
  43. package/pervasives.gr +2 -2
  44. package/pervasives.md +37 -37
  45. package/priorityqueue.gr +7 -7
  46. package/priorityqueue.md +19 -19
  47. package/queue.gr +183 -29
  48. package/queue.md +296 -40
  49. package/random.md +6 -6
  50. package/range.gr +4 -4
  51. package/range.md +6 -6
  52. package/rational.md +16 -16
  53. package/regex.gr +52 -51
  54. package/regex.md +11 -11
  55. package/result.md +16 -16
  56. package/runtime/atof/common.md +39 -39
  57. package/runtime/atof/decimal.gr +6 -6
  58. package/runtime/atof/decimal.md +8 -8
  59. package/runtime/atof/lemire.gr +5 -5
  60. package/runtime/atof/lemire.md +1 -1
  61. package/runtime/atof/parse.gr +16 -16
  62. package/runtime/atof/parse.md +2 -2
  63. package/runtime/atof/slow.md +1 -1
  64. package/runtime/atof/table.md +2 -2
  65. package/runtime/atoi/parse.gr +3 -3
  66. package/runtime/atoi/parse.md +1 -1
  67. package/runtime/bigint.gr +15 -47
  68. package/runtime/bigint.md +54 -60
  69. package/runtime/compare.gr +2 -2
  70. package/runtime/compare.md +1 -1
  71. package/runtime/dataStructures.md +33 -33
  72. package/runtime/debugPrint.gr +4 -1
  73. package/runtime/debugPrint.md +9 -9
  74. package/runtime/equal.gr +99 -77
  75. package/runtime/equal.md +1 -1
  76. package/runtime/exception.gr +62 -82
  77. package/runtime/exception.md +62 -11
  78. package/runtime/gc.gr +39 -45
  79. package/runtime/gc.md +4 -4
  80. package/runtime/malloc.gr +7 -7
  81. package/runtime/malloc.md +4 -4
  82. package/runtime/math/kernel/cos.gr +70 -0
  83. package/runtime/math/kernel/cos.md +14 -0
  84. package/runtime/math/kernel/sin.gr +65 -0
  85. package/runtime/math/kernel/sin.md +14 -0
  86. package/runtime/math/kernel/tan.gr +136 -0
  87. package/runtime/math/kernel/tan.md +14 -0
  88. package/runtime/math/rempio2.gr +244 -0
  89. package/runtime/math/rempio2.md +14 -0
  90. package/runtime/math/trig.gr +130 -0
  91. package/runtime/math/trig.md +28 -0
  92. package/runtime/math/umuldi.gr +26 -0
  93. package/runtime/math/umuldi.md +14 -0
  94. package/runtime/numberUtils.gr +29 -29
  95. package/runtime/numberUtils.md +12 -12
  96. package/runtime/numbers.gr +373 -381
  97. package/runtime/numbers.md +79 -73
  98. package/runtime/string.gr +37 -105
  99. package/runtime/string.md +3 -9
  100. package/runtime/unsafe/constants.md +24 -24
  101. package/runtime/unsafe/conv.md +13 -13
  102. package/runtime/unsafe/memory.gr +24 -20
  103. package/runtime/unsafe/memory.md +27 -7
  104. package/runtime/unsafe/offsets.gr +36 -0
  105. package/runtime/unsafe/offsets.md +88 -0
  106. package/runtime/unsafe/panic.gr +28 -0
  107. package/runtime/unsafe/panic.md +14 -0
  108. package/runtime/unsafe/tags.md +32 -32
  109. package/runtime/unsafe/wasmf32.md +28 -28
  110. package/runtime/unsafe/wasmf64.md +28 -28
  111. package/runtime/unsafe/wasmi32.md +47 -47
  112. package/runtime/unsafe/wasmi64.md +50 -50
  113. package/runtime/utf8.gr +189 -0
  114. package/runtime/utf8.md +117 -0
  115. package/runtime/wasi.gr +4 -2
  116. package/runtime/wasi.md +138 -138
  117. package/set.gr +18 -11
  118. package/set.md +42 -36
  119. package/stack.gr +171 -2
  120. package/stack.md +297 -15
  121. package/string.gr +352 -557
  122. package/string.md +77 -34
  123. package/uint16.md +22 -22
  124. package/uint32.gr +25 -4
  125. package/uint32.md +63 -28
  126. package/uint64.gr +25 -5
  127. package/uint64.md +63 -28
  128. package/uint8.md +22 -22
  129. package/uri.gr +57 -53
  130. package/uri.md +11 -12
  131. package/wasi/file.gr +67 -59
  132. package/wasi/file.md +39 -39
  133. package/wasi/process.md +5 -5
  134. package/wasi/random.md +3 -3
  135. package/wasi/time.md +4 -4
  136. package/runtime/utils/printing.gr +0 -60
  137. package/runtime/utils/printing.md +0 -26
package/list.gr CHANGED
@@ -13,6 +13,24 @@ from "runtime/unsafe/memory" include Memory
13
13
  from "runtime/unsafe/wasmi32" include WasmI32
14
14
  from "runtime/dataStructures" include DataStructures
15
15
 
16
+ /**
17
+ * Creates a new list with all elements in reverse order.
18
+ *
19
+ * @param list: The list to reverse
20
+ * @returns The new list
21
+ *
22
+ * @since v0.1.0
23
+ */
24
+ provide let reverse = list => {
25
+ let rec revHelp = (list, acc) => {
26
+ match (list) {
27
+ [] => acc,
28
+ [first, ...rest] => revHelp(rest, [first, ...acc]),
29
+ }
30
+ }
31
+ revHelp(list, [])
32
+ }
33
+
16
34
  /**
17
35
  * Creates a new list of the specified length where each element is
18
36
  * initialized with the result of an initializer function. The initializer
@@ -27,17 +45,18 @@ from "runtime/dataStructures" include DataStructures
27
45
  * @since v0.3.0
28
46
  */
29
47
  provide let init = (length, fn) => {
30
- // This method can be further improved by checking the length against a specific size
31
- // and determining if it can be made tail-recursive, then use List.reverse on it.
32
- // Which would be the same as OCaml does it in https://github.com/ocaml/ocaml/blob/03839754f46319aa36d9dad56940a6f3c3bcb48a/stdlib/list.ml#L79
33
- let rec iter = (idx, length) => {
34
- if (idx >= length) {
35
- []
36
- } else {
37
- [fn(idx), ...iter(idx + 1, length)]
48
+ if (length < 2048) {
49
+ // For small lists, use a faster non-tail-recursive version
50
+ let rec iter = (idx, length) => {
51
+ if (idx >= length) [] else [fn(idx), ...iter(idx + 1, length)]
52
+ }
53
+ iter(0, length)
54
+ } else {
55
+ let rec iter = (idx, length, acc) => {
56
+ if (idx >= length) acc else iter(idx + 1, length, [fn(idx), ...acc])
38
57
  }
58
+ reverse(iter(0, length, []))
39
59
  }
40
- iter(0, length)
41
60
  }
42
61
 
43
62
  /**
@@ -74,24 +93,6 @@ provide let isEmpty = list => {
74
93
  }
75
94
  }
76
95
 
77
- /**
78
- * Creates a new list with all elements in reverse order.
79
- *
80
- * @param list: The list to reverse
81
- * @returns The new list
82
- *
83
- * @since v0.1.0
84
- */
85
- provide let reverse = list => {
86
- let rec iter = (list, acc) => {
87
- match (list) {
88
- [] => acc,
89
- [first, ...rest] => iter(rest, [first, ...acc]),
90
- }
91
- }
92
- iter(list, [])
93
- }
94
-
95
96
  /**
96
97
  * Creates a new list with the elements of the first list followed by
97
98
  * the elements of the second list.
@@ -217,6 +218,56 @@ provide let mapi = (fn, list) => {
217
218
  iter(fn, list, 0)
218
219
  }
219
220
 
221
+ /**
222
+ * Produces a new list initialized with the results of a mapper function
223
+ * called on each element of the input list.
224
+ * The mapper function can return `None` to exclude the element from the new list.
225
+ *
226
+ * @param fn: The mapper function to call on each element, where the value returned will be used to initialize the element in the new list
227
+ * @param list: The list to iterate
228
+ * @returns The new list with filtered mapped values
229
+ *
230
+ * @example List.filterMap(x => if (x % 2 == 0) Some(toString(x)) else None, [1, 2, 3, 4]) == ["2", "4"]
231
+ *
232
+ * @since v0.7.0
233
+ */
234
+ provide let rec filterMap = (fn, list) => {
235
+ match (list) {
236
+ [] => [],
237
+ [first, ...rest] => match (fn(first)) {
238
+ Some(v) => [v, ...filterMap(fn, rest)],
239
+ None => filterMap(fn, rest),
240
+ },
241
+ }
242
+ }
243
+
244
+ /**
245
+ * Produces a new list initialized with the results of a mapper function
246
+ * called on each element of the input list and its index.
247
+ * The mapper function can return `None` to exclude the element from the new list.
248
+ *
249
+ * @param fn: The mapper function to call on each element, where the value returned will be used to initialize the element in the new list
250
+ * @param list: The list to iterate
251
+ * @returns The new list with filtered mapped values
252
+ *
253
+ * @example List.filterMapi((x, i) => if (x % 2 == 0) Some(toString(x)) else None, [1, 2, 3, 4]) == ["2", "4"]
254
+ * @example List.filterMapi((x, i) => if (i == 0) Some(toString(x)) else None, [1, 2, 3, 4]) == ["1"]
255
+ *
256
+ * @since v0.7.0
257
+ */
258
+ provide let filterMapi = (fn, list) => {
259
+ let rec iter = (fn, list, index) => {
260
+ match (list) {
261
+ [] => [],
262
+ [first, ...rest] => match (fn(first, index)) {
263
+ Some(v) => [v, ...iter(fn, rest, index + 1)],
264
+ None => iter(fn, rest, index + 1),
265
+ },
266
+ }
267
+ }
268
+ iter(fn, list, 0)
269
+ }
270
+
220
271
  /**
221
272
  * Produces a new list by calling a function on each element
222
273
  * of the input list. Each iteration produces an intermediate
@@ -242,7 +293,7 @@ provide let rec flatMap = (fn, list) => {
242
293
  *
243
294
  * @param fn: The function to call on each element, where the returned value indicates if the element satisfies the condition
244
295
  * @param list: The list to check
245
- * @returns `true` if all elements satify the condition or `false` otherwise
296
+ * @returns `true` if all elements satisfy the condition or `false` otherwise
246
297
  *
247
298
  * @since v0.1.0
248
299
  */
@@ -260,7 +311,7 @@ provide let rec every = (fn, list) => {
260
311
  *
261
312
  * @param fn: The function to call on each element, where the returned value indicates if the element satisfies the condition
262
313
  * @param list: The list to iterate
263
- * @returns `true` if one or more elements satify the condition or `false` otherwise
314
+ * @returns `true` if one or more elements satisfy the condition or `false` otherwise
264
315
  *
265
316
  * @since v0.1.0
266
317
  */
@@ -769,7 +820,7 @@ provide let rec takeWhile = (fn, list) => {
769
820
  }
770
821
 
771
822
  /**
772
- * Finds the first element in a list that satifies the given condition.
823
+ * Finds the first element in a list that satisfies the given condition.
773
824
  *
774
825
  * @param fn: The function to call on each element, where the returned value indicates if the element satisfies the condition
775
826
  * @param list: The list to search
@@ -787,7 +838,7 @@ provide let rec find = (fn, list) => {
787
838
  }
788
839
 
789
840
  /**
790
- * Finds the first index in a list where the element satifies the given condition.
841
+ * Finds the first index in a list where the element satisfies the given condition.
791
842
  *
792
843
  * @param fn: The function to call on each element, where the returned value indicates if the element satisfies the condition
793
844
  * @param list: The list to search
@@ -808,6 +859,31 @@ provide let findIndex = (fn, list) => {
808
859
  findItemIndex(list, 0)
809
860
  }
810
861
 
862
+ /**
863
+ * Finds the first element in a list that satisfies the given condition and
864
+ * returns the result of applying a mapper function to it.
865
+ *
866
+ * @param fn: The function to call on each element, where the returned value indicates if the element satisfies the condition
867
+ * @param list: The list to search
868
+ * @returns `Some(mapped)` containing the first value found with the given mapping or `None` otherwise
869
+ *
870
+ * @example
871
+ * let jsonObject = [(1, 'a'), (2, 'b'), (1, 'c')]
872
+ * let getItem = (key, obj) => List.findMap(((k, v)) => if (k == key) Some(v) else None, obj)
873
+ * assert getItem(1, jsonObject) == Some('a')
874
+ *
875
+ * @since v0.7.0
876
+ */
877
+ provide let rec findMap = (fn, list) => {
878
+ match (list) {
879
+ [] => None,
880
+ [first, ...rest] => match (fn(first)) {
881
+ None => findMap(fn, rest),
882
+ Some(v) => Some(v),
883
+ },
884
+ }
885
+ }
886
+
811
887
  /**
812
888
  * Combines two lists into a Cartesian product of tuples containing
813
889
  * all ordered pairs `(a, b)`.
@@ -972,3 +1048,224 @@ provide let sort = (compare=compare, list) => {
972
1048
 
973
1049
  mergesort(list)
974
1050
  }
1051
+
1052
+ /**
1053
+ * Utilities for working with lists of key-key value pairs.
1054
+ *
1055
+ * @example
1056
+ * let data = [
1057
+ * ("name", "Alice"),
1058
+ * ("age", "30"),
1059
+ * ]
1060
+ * assert List.Associative.get("name", data) == Some("Alice")
1061
+ *
1062
+ * @since v0.7.0
1063
+ */
1064
+ provide module Associative {
1065
+ /**
1066
+ * Checks if the given key is present in the list of key-value pairs.
1067
+ *
1068
+ * @param key: The key to search for
1069
+ * @param list: The list of key-value pairs
1070
+ *
1071
+ * @returns `true` if the key is found or `false` otherwise
1072
+ *
1073
+ * @example
1074
+ * let data = [
1075
+ * ("name", "Alice"),
1076
+ * ("age", "30"),
1077
+ * ]
1078
+ * assert List.Associative.has("name", data) == true
1079
+ * @example List.Associative.has("age", []) == false
1080
+ *
1081
+ * @since v0.7.0
1082
+ */
1083
+ provide let rec has = (key, list) => {
1084
+ match (list) {
1085
+ [] => false,
1086
+ [(k, _), ...rest] when key == k => true,
1087
+ [_, ...rest] => has(key, rest),
1088
+ }
1089
+ }
1090
+
1091
+ /**
1092
+ * Retrieves the first value in the list of key-value pairs that matches the given key.
1093
+ *
1094
+ * @param key: The key to search for
1095
+ * @param list: The list of key-value pairs
1096
+ *
1097
+ * @returns `Some(value)` if the key is found or `None` otherwise
1098
+ *
1099
+ * @example
1100
+ * let data = [
1101
+ * ("name", "Alice"),
1102
+ * ("name", "Bob"),
1103
+ * ("age", "30"),
1104
+ * ]
1105
+ * assert List.Associative.get("name", data) == Some("Alice")
1106
+ *
1107
+ * @example List.Associative.get("age", []) == None
1108
+ *
1109
+ * @since v0.7.0
1110
+ */
1111
+ provide let rec get = (key, list) => {
1112
+ match (list) {
1113
+ [] => None,
1114
+ [(k, v), ...rest] when key == k => Some(v),
1115
+ [_, ...rest] => get(key, rest),
1116
+ }
1117
+ }
1118
+
1119
+ /**
1120
+ * Retrieves all values in the list of key-value pairs that match the given key.
1121
+ *
1122
+ * @param key: The key to search for
1123
+ * @param list: The list of key-value pairs
1124
+ *
1125
+ * @returns An array of values matching the given key
1126
+ *
1127
+ * @example
1128
+ * let data = [
1129
+ * ("name", "Alice"),
1130
+ * ("name", "Bob"),
1131
+ * ("age", "30"),
1132
+ * ]
1133
+ * assert List.Associative.getAll("name", data) == [
1134
+ * "Alice",
1135
+ * "Bob"
1136
+ * ]
1137
+ *
1138
+ * @example List.Associative.getAll("age", []) == []
1139
+ *
1140
+ * @since v0.7.0
1141
+ */
1142
+ provide let rec getAll = (key, list) => {
1143
+ match (list) {
1144
+ [] => [],
1145
+ [(k, v), ...rest] when key == k => [v, ...getAll(key, rest)],
1146
+ [_, ...rest] => getAll(key, rest),
1147
+ }
1148
+ }
1149
+
1150
+ /**
1151
+ * Creates a new list with the first value in the list of key-value pairs that matches the key replaced.
1152
+ * If the key is not found the item is appended to the list.
1153
+ *
1154
+ * @param key: The key to replace
1155
+ * @param value: The new value to set
1156
+ * @param list: The list of key-value pairs
1157
+ *
1158
+ * @returns A new list with the key-value pair replaced
1159
+ *
1160
+ * @example
1161
+ * let data = [
1162
+ * ("name", "Alice"),
1163
+ * ("name", "Bob"),
1164
+ * ("age", "30"),
1165
+ * ]
1166
+ * assert List.Associative.set("name", "Charlie", data) == [("name", "Charlie"), ("name", "Bob"), ("age", "30")]
1167
+ *
1168
+ * @example List.Associative.set("age", "30", [("name", "Alice")]) == [("name", "Alice"), ("age", "30")]
1169
+ *
1170
+ * @since v0.7.0
1171
+ */
1172
+ provide let rec set = (key, value, list) => {
1173
+ match (list) {
1174
+ [] => [(key, value)],
1175
+ [(k, _), ...rest] when key == k => [(k, value), ...rest],
1176
+ [first, ...rest] => [first, ...set(key, value, rest)],
1177
+ }
1178
+ }
1179
+
1180
+ let rec setAllHelp = (key, value, list, hitMatch=false) => {
1181
+ match (list) {
1182
+ [] when !hitMatch => [(key, value)],
1183
+ [] => [],
1184
+ [(k, _), ...rest] when key == k =>
1185
+ [(k, value), ...setAllHelp(key, value, rest, hitMatch=true)],
1186
+ [first, ...rest] =>
1187
+ [first, ...setAllHelp(key, value, rest, hitMatch=hitMatch)],
1188
+ }
1189
+ }
1190
+ /**
1191
+ * Creates a new list with all values in the list of key-value pairs that match the key replaced.
1192
+ * If the key is not found the item is appended to the list.
1193
+ *
1194
+ * @param key: The key to replace
1195
+ * @param value: The new value to set
1196
+ * @param list: The list of key-value pairs
1197
+ *
1198
+ * @returns A new list with the key-value pairs replaced
1199
+ *
1200
+ * @example
1201
+ * let data = [
1202
+ * ("name", "Alice"),
1203
+ * ("name", "Bob"),
1204
+ * ("age", "30"),
1205
+ * ]
1206
+ * assert List.Associative.setAll("name", "Charlie", data) == [("name", "Charlie"), ("name", "Charlie"), ("age", "30")]
1207
+ *
1208
+ * @example List.Associative.setAll("age", "30", [("name", "Alice")]) == [("name", "Alice"), ("age", "30")]
1209
+ *
1210
+ * @since v0.7.0
1211
+ */
1212
+ provide let setAll = (key, value, list) => setAllHelp(key, value, list)
1213
+
1214
+ /**
1215
+ * Creates a new list with the first value in the list of key-value pairs that matches the key removed.
1216
+ * If the key is not found, the list is returned unchanged.
1217
+ *
1218
+ * @param key: The key to remove
1219
+ * @param list: The list of key-value pairs
1220
+ *
1221
+ * @returns The new list with the key-value pair removed
1222
+ *
1223
+ * @example
1224
+ * let data = [
1225
+ * ("name", "Alice"),
1226
+ * ("name", "Bob"),
1227
+ * ("age", "30"),
1228
+ * ]
1229
+ * assert List.Associative.remove("name", data) == [("name", "Bob"), ("age", "30")]
1230
+ *
1231
+ * @example List.Associative.remove("age", [("name", "Alice")]) == []
1232
+ *
1233
+ * @since v0.7.0
1234
+ */
1235
+ provide let rec remove = (key, list) => {
1236
+ match (list) {
1237
+ [] => [],
1238
+ [(k, v), ...rest] when key == k => rest,
1239
+ [first, ...rest] => [first, ...remove(key, rest)],
1240
+ }
1241
+ }
1242
+
1243
+ /**
1244
+ * Creates a new list with all values in the list of key-value pairs matching the key removed.
1245
+ * If the key is not found, the list is returned unchanged.
1246
+ *
1247
+ * @param key: The key to remove
1248
+ * @param list: The list of key-value pairs
1249
+ *
1250
+ * @returns The new list with the key-value pairs removed
1251
+ *
1252
+ * @example
1253
+ * let data = [
1254
+ * ("name", "Alice"),
1255
+ * ("name", "Bob"),
1256
+ * ("age", "30"),
1257
+ * ]
1258
+ * assert List.Associative.removeAll("name", data) == [("age", "30")]
1259
+ *
1260
+ * @example List.Associative.removeAll("age", [("name", "Alice")]) == [("name", "Alice")]
1261
+ *
1262
+ * @since v0.7.0
1263
+ */
1264
+ provide let rec removeAll = (key, list) => {
1265
+ match (list) {
1266
+ [] => [],
1267
+ [(k, v), ...rest] when key == k => removeAll(key, rest),
1268
+ [first, ...rest] => [first, ...removeAll(key, rest)],
1269
+ }
1270
+ }
1271
+ }