@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
package/pervasives.gr CHANGED
@@ -473,10 +473,11 @@ export primitive ignore: a -> Void = "@ignore"
473
473
 
474
474
  /**
475
475
  * Assert that the given Boolean condition is `true`.
476
- * Throws an `AssertionError` if the condition is `false`.
477
476
  *
478
477
  * @param condition: The condition to assert
479
478
  *
479
+ * @throws AssertionError: When the `condition` is false
480
+ *
480
481
  * @example assert 3 > 2
481
482
  * @example assert true
482
483
  *
@@ -578,7 +579,7 @@ export let cons = (a, b) =>
578
579
  export let empty = [] // <- for parity with `cons`, but should be deleted as well
579
580
 
580
581
  // Setup exception printing
581
- @disableGC
582
+ @unsafe
582
583
  let rec setupExceptions = () => {
583
584
  Exception.dangerouslyRegisterPrinter(e => {
584
585
  match (e) {
@@ -589,8 +590,6 @@ let rec setupExceptions = () => {
589
590
  })
590
591
 
591
592
  Exception.dangerouslyRegisterBasePrinter(e => Some(toString(e)))
592
- Memory.decRef(WasmI32.fromGrain(setupExceptions))
593
- void
594
593
  }
595
594
 
596
595
  setupExceptions()
package/pervasives.md CHANGED
@@ -942,7 +942,6 @@ assert : Bool -> Void
942
942
  ```
943
943
 
944
944
  Assert that the given Boolean condition is `true`.
945
- Throws an `AssertionError` if the condition is `false`.
946
945
 
947
946
  Parameters:
948
947
 
@@ -950,6 +949,12 @@ Parameters:
950
949
  |-----|----|-----------|
951
950
  |`condition`|`Bool`|The condition to assert|
952
951
 
952
+ Throws:
953
+
954
+ `AssertionError`
955
+
956
+ * When the `condition` is false
957
+
953
958
  Examples:
954
959
 
955
960
  ```grain
package/priorityqueue.gr CHANGED
@@ -219,6 +219,29 @@ export let drain = pq => {
219
219
  List.reverse(drainRec([]))
220
220
  }
221
221
 
222
+ /**
223
+ * Constructs a new priority queue initialized with the elements in the array
224
+ * using a custom comparator function, which is used to determine priority of
225
+ * elements. The comparator function takes two elements and must return 0 if
226
+ * both share priority, a positive number if the first has greater priority,
227
+ * and a negative number if the first has less priority.
228
+ *
229
+ * @param array: An array of values used to initialize the priority queue
230
+ * @param comp: A comparator function used to assign priority to elements
231
+ * @returns A priority queue containing the elements from the array
232
+ *
233
+ * @since v0.5.4
234
+ */
235
+ export let fromArray = (array, comp) => {
236
+ let size = Array.length(array)
237
+ let array = Array.map(x => Some(x), array)
238
+ let heap = { size, array, comp }
239
+ for (let mut i = size - 1; i >= 0; i -= 1) {
240
+ siftDown(i, heap)
241
+ }
242
+ heap
243
+ }
244
+
222
245
  /**
223
246
  * Constructs a new priority queue initialized with the elements in the list
224
247
  * using a custom comparator function, which is used to determine priority of
@@ -233,9 +256,6 @@ export let drain = pq => {
233
256
  * @since v0.5.3
234
257
  */
235
258
  export let fromList = (list, comp) => {
236
- let heap = makeSized(List.length(list), comp)
237
- List.forEach(val => {
238
- push(val, heap)
239
- }, list)
240
- heap
259
+ let array = Array.fromList(list)
260
+ fromArray(array, comp)
241
261
  }
package/priorityqueue.md CHANGED
@@ -247,6 +247,36 @@ Returns:
247
247
  |----|-----------|
248
248
  |`List<a>`|A list of all elements in the priority in priority order|
249
249
 
250
+ ### PriorityQueue.**fromArray**
251
+
252
+ <details disabled>
253
+ <summary tabindex="-1">Added in <code>0.5.4</code></summary>
254
+ No other changes yet.
255
+ </details>
256
+
257
+ ```grain
258
+ fromArray : (Array<a>, ((a, a) -> Number)) -> PriorityQueue<a>
259
+ ```
260
+
261
+ Constructs a new priority queue initialized with the elements in the array
262
+ using a custom comparator function, which is used to determine priority of
263
+ elements. The comparator function takes two elements and must return 0 if
264
+ both share priority, a positive number if the first has greater priority,
265
+ and a negative number if the first has less priority.
266
+
267
+ Parameters:
268
+
269
+ |param|type|description|
270
+ |-----|----|-----------|
271
+ |`array`|`Array<a>`|An array of values used to initialize the priority queue|
272
+ |`comp`|`(a, a) -> Number`|A comparator function used to assign priority to elements|
273
+
274
+ Returns:
275
+
276
+ |type|description|
277
+ |----|-----------|
278
+ |`PriorityQueue<a>`|A priority queue containing the elements from the array|
279
+
250
280
  ### PriorityQueue.**fromList**
251
281
 
252
282
  <details disabled>
package/queue.gr CHANGED
@@ -2,6 +2,8 @@
2
2
  * @module Queue: An immutable queue implementation. A queue is a FIFO (first-in-first-out) data structure where new values are added to the end and retrieved or removed from the beginning.
3
3
  * @example import Queue from "queue"
4
4
  * @since v0.2.0
5
+ *
6
+ * @deprecated This module will be renamed to ImmutableQueue in the v0.6.0 release of Grain.
5
7
  */
6
8
  import List from "list"
7
9
 
@@ -15,13 +17,26 @@ record Queue<a> {
15
17
  }
16
18
 
17
19
  /**
18
- * @section Values: Functions for working with queues.
20
+ * @section Values: Functions and constants for working with queues.
19
21
  */
20
22
 
23
+ /**
24
+ * An empty queue.
25
+ *
26
+ * @since v0.5.4
27
+ */
28
+ export let empty = {
29
+ let empty = { forwards: [], backwards: [] }
30
+ empty
31
+ }
32
+
21
33
  /**
22
34
  * Creates an empty queue.
23
- *
35
+ *
24
36
  * @returns An empty queue
37
+ *
38
+ * @deprecated This will be removed in the v0.6.0 release of Grain.
39
+ *
25
40
  * @since v0.2.0
26
41
  */
27
42
  export let make = () => {
@@ -30,7 +45,7 @@ export let make = () => {
30
45
 
31
46
  /**
32
47
  * Checks if the given queue contains any values.
33
- *
48
+ *
34
49
  * @param queue: The queue to check
35
50
  * @returns `true` if the given queue is empty or `false` otherwise
36
51
  *
@@ -45,7 +60,7 @@ export let isEmpty = queue => {
45
60
 
46
61
  /**
47
62
  * Returns the value at the beginning of the queue. It is not removed from the queue.
48
- *
63
+ *
49
64
  * @param queue: The queue to inspect
50
65
  * @returns `Some(value)` containing the value at the beginning of the queue, or `None` if the queue is empty
51
66
  *
@@ -63,7 +78,7 @@ export let peek = queue => {
63
78
 
64
79
  /**
65
80
  * Adds a value to the end of the queue.
66
- *
81
+ *
67
82
  * @param value: The value to append
68
83
  * @param queue: The queue to update
69
84
  * @returns An updated queue
@@ -82,7 +97,7 @@ export let push = (value, queue) => {
82
97
 
83
98
  /**
84
99
  * Dequeues the next value in the queue.
85
- *
100
+ *
86
101
  * @param queue: The queue to change
87
102
  * @returns An updated queue
88
103
  *
@@ -106,7 +121,7 @@ export let pop = queue => {
106
121
 
107
122
  /**
108
123
  * Get the number of values in a queue.
109
- *
124
+ *
110
125
  * @param queue: The queue to inspect
111
126
  * @returns The number of values in the queue
112
127
  *
package/queue.md CHANGED
@@ -2,6 +2,8 @@
2
2
  title: Queue
3
3
  ---
4
4
 
5
+ > **Deprecated:** This module will be renamed to ImmutableQueue in the v0.6.0 release of Grain.
6
+
5
7
  An immutable queue implementation. A queue is a FIFO (first-in-first-out) data structure where new values are added to the end and retrieved or removed from the beginning.
6
8
 
7
9
  <details disabled>
@@ -25,10 +27,25 @@ type Queue<a>
25
27
 
26
28
  ## Values
27
29
 
28
- Functions for working with queues.
30
+ Functions and constants for working with queues.
31
+
32
+ ### Queue.**empty**
33
+
34
+ <details disabled>
35
+ <summary tabindex="-1">Added in <code>0.5.4</code></summary>
36
+ No other changes yet.
37
+ </details>
38
+
39
+ ```grain
40
+ empty : Queue<a>
41
+ ```
42
+
43
+ An empty queue.
29
44
 
30
45
  ### Queue.**make**
31
46
 
47
+ > **Deprecated:** This will be removed in the v0.6.0 release of Grain.
48
+
32
49
  <details disabled>
33
50
  <summary tabindex="-1">Added in <code>0.2.0</code></summary>
34
51
  No other changes yet.
package/regex.gr CHANGED
@@ -121,10 +121,10 @@ let withConfig = (buf: RegExBuf, config: RegExParserConfig) => {
121
121
 
122
122
  let parseErr = (buf: RegExBuf, msg: String, posShift) => {
123
123
  "Invalid Regular Expression: " ++
124
- msg ++
125
- " (position " ++
126
- toString(unbox(buf.cursor) + posShift) ++
127
- ")"
124
+ msg ++
125
+ " (position " ++
126
+ toString(unbox(buf.cursor) + posShift) ++
127
+ ")"
128
128
  }
129
129
 
130
130
  let next = (buf: RegExBuf) => {
@@ -241,7 +241,8 @@ let rec rangeAdd = (rng: CharRange, v: CharRangeElt) => {
241
241
  _ when rangeContains(rng, v) => rng,
242
242
  _ => rangeUnion(rng, [(v, v)]),
243
243
  }
244
- }, rangeUnion = (rng1, rng2) => {
244
+ },
245
+ rangeUnion = (rng1, rng2) => {
245
246
  match ((rng1, rng2)) {
246
247
  ([], _) => rng2,
247
248
  (_, []) => rng1,
@@ -457,7 +458,10 @@ enum ParsedRegularExpression {
457
458
  ), // regex, n-start, num-n, needs-backtrack
458
459
  REReference(Number, Bool), // n, case-sensitive
459
460
  RERange(RERange),
460
- REUnicodeCategories(List<UnicodeCategory>, Bool), // symlist, true=match/false=does-not-match
461
+ REUnicodeCategories(
462
+ List<UnicodeCategory>,
463
+ Bool
464
+ ), // symlist, true=match/false=does-not-match
461
465
  }
462
466
 
463
467
  let needsBacktrack = (rx: ParsedRegularExpression) => {
@@ -636,7 +640,8 @@ let rec parseRangeNot = (buf: RegExBuf) => {
636
640
  Ok(_) => parseRange(buf),
637
641
  }
638
642
  }
639
- }, parseRange = (buf: RegExBuf) => {
643
+ },
644
+ parseRange = (buf: RegExBuf) => {
640
645
  if (!more(buf)) {
641
646
  Err(parseErr(buf, "Missing closing `]`", 0))
642
647
  } else {
@@ -659,7 +664,8 @@ let rec parseRangeNot = (buf: RegExBuf) => {
659
664
  Ok(_) => parseRangeRest(buf, [], None, None),
660
665
  }
661
666
  }
662
- }, parseClass = (buf: RegExBuf) => {
667
+ },
668
+ parseClass = (buf: RegExBuf) => {
663
669
  if (!more(buf)) {
664
670
  Err(
665
671
  "no chars"
@@ -694,7 +700,8 @@ let rec parseRangeNot = (buf: RegExBuf) => {
694
700
  Ok(c) => Err("unknown class: " ++ toString(c)),
695
701
  }
696
702
  }
697
- }, parsePosixCharClass = (buf: RegExBuf) => {
703
+ },
704
+ parsePosixCharClass = (buf: RegExBuf) => {
698
705
  if (!more(buf)) {
699
706
  Err(parseErr(buf, "Missing POSIX character class after `[`", 0))
700
707
  } else {
@@ -810,7 +817,8 @@ let rec parseRangeNot = (buf: RegExBuf) => {
810
817
  ),
811
818
  }
812
819
  }
813
- }, parseRangeRest =
820
+ },
821
+ parseRangeRest =
814
822
  (
815
823
  buf: RegExBuf,
816
824
  rng: CharRange,
@@ -962,7 +970,8 @@ let rec parseRangeNot = (buf: RegExBuf) => {
962
970
  },
963
971
  }
964
972
  }
965
- }, parseRangeRestSpan =
973
+ },
974
+ parseRangeRestSpan =
966
975
  (
967
976
  buf: RegExBuf,
968
977
  c,
@@ -1213,7 +1222,8 @@ let rec parseAtom = (buf: RegExBuf) => {
1213
1222
  _ => parseLiteral(buf),
1214
1223
  },
1215
1224
  }
1216
- }, parseLook = (buf: RegExBuf) => {
1225
+ },
1226
+ parseLook = (buf: RegExBuf) => {
1217
1227
  let preNumGroups = unbox(buf.config.groupNumber)
1218
1228
  let spanNumGroups = () => unbox(buf.config.groupNumber) - preNumGroups
1219
1229
  // (isMatch, isAhead)
@@ -1279,7 +1289,8 @@ let rec parseAtom = (buf: RegExBuf) => {
1279
1289
  }
1280
1290
  },
1281
1291
  }
1282
- }, parseTest = (buf: RegExBuf) => {
1292
+ },
1293
+ parseTest = (buf: RegExBuf) => {
1283
1294
  if (!more(buf)) {
1284
1295
  Err(parseErr(buf, "Expected test", 0))
1285
1296
  } else {
@@ -1316,7 +1327,8 @@ let rec parseAtom = (buf: RegExBuf) => {
1316
1327
  ),
1317
1328
  }
1318
1329
  }
1319
- }, parseInteger = (buf: RegExBuf, n) => {
1330
+ },
1331
+ parseInteger = (buf: RegExBuf, n) => {
1320
1332
  if (!more(buf)) {
1321
1333
  Ok(n)
1322
1334
  } else {
@@ -1331,7 +1343,8 @@ let rec parseAtom = (buf: RegExBuf) => {
1331
1343
  Ok(_) => Ok(n),
1332
1344
  }
1333
1345
  }
1334
- }, parseMode = (buf: RegExBuf) => {
1346
+ },
1347
+ parseMode = (buf: RegExBuf) => {
1335
1348
  let processState = ((cs, ml)) => {
1336
1349
  let withCs = match (cs) {
1337
1350
  None => buf.config,
@@ -1390,7 +1403,8 @@ let rec parseAtom = (buf: RegExBuf) => {
1390
1403
  }
1391
1404
  }
1392
1405
  help((None, None))
1393
- }, parseUnicodeCategories = (buf: RegExBuf, pC: String) => {
1406
+ },
1407
+ parseUnicodeCategories = (buf: RegExBuf, pC: String) => {
1394
1408
  if (!more(buf)) {
1395
1409
  Err(parseErr(buf, "Expected unicode category", 0))
1396
1410
  } else {
@@ -1558,7 +1572,8 @@ let rec parseAtom = (buf: RegExBuf) => {
1558
1572
  Ok(_) => Err(parseErr(buf, "Expected `{` after `\\" ++ pC ++ "`", 0)),
1559
1573
  }
1560
1574
  }
1561
- }, parseLiteral = (buf: RegExBuf) => {
1575
+ },
1576
+ parseLiteral = (buf: RegExBuf) => {
1562
1577
  if (!more(buf)) {
1563
1578
  Err(parseErr(buf, "Expected literal", 0))
1564
1579
  } else {
@@ -1592,7 +1607,8 @@ let rec parseAtom = (buf: RegExBuf) => {
1592
1607
  },
1593
1608
  }
1594
1609
  }
1595
- }, parseBackslashLiteral = (buf: RegExBuf) => {
1610
+ },
1611
+ parseBackslashLiteral = (buf: RegExBuf) => {
1596
1612
  if (!more(buf)) {
1597
1613
  // Special case: EOS after backslash matches null
1598
1614
  Err(parseErr(buf, "Expected to find escaped value after backslash", 0))
@@ -1655,7 +1671,8 @@ let rec parseAtom = (buf: RegExBuf) => {
1655
1671
  },
1656
1672
  }
1657
1673
  }
1658
- }, parseNonGreedy = (buf: RegExBuf) => {
1674
+ },
1675
+ parseNonGreedy = (buf: RegExBuf) => {
1659
1676
  let checkNotNested = res => {
1660
1677
  if (!more(buf)) {
1661
1678
  res
@@ -1681,7 +1698,8 @@ let rec parseAtom = (buf: RegExBuf) => {
1681
1698
  Ok(_) => checkNotNested(Ok(false)),
1682
1699
  }
1683
1700
  }
1684
- }, parsePCE = (buf: RegExBuf) => {
1701
+ },
1702
+ parsePCE = (buf: RegExBuf) => {
1685
1703
  match (parseAtom(buf)) {
1686
1704
  Err(e) => Err(e),
1687
1705
  Ok(atom) => {
@@ -1775,7 +1793,8 @@ let rec parseAtom = (buf: RegExBuf) => {
1775
1793
  }
1776
1794
  },
1777
1795
  }
1778
- }, parsePCEs = (buf: RegExBuf, toplevel: Bool) => {
1796
+ },
1797
+ parsePCEs = (buf: RegExBuf, toplevel: Bool) => {
1779
1798
  if (!more(buf)) {
1780
1799
  Ok([])
1781
1800
  } else {
@@ -1801,7 +1820,8 @@ let rec parseAtom = (buf: RegExBuf) => {
1801
1820
  },
1802
1821
  }
1803
1822
  }
1804
- }, parseRegex = (buf: RegExBuf) => {
1823
+ },
1824
+ parseRegex = (buf: RegExBuf) => {
1805
1825
  if (!more(buf)) {
1806
1826
  Ok(REEmpty)
1807
1827
  } else {
@@ -1836,7 +1856,8 @@ let rec parseAtom = (buf: RegExBuf) => {
1836
1856
  },
1837
1857
  }
1838
1858
  }
1839
- }, parseRegexNonEmpty = (buf: RegExBuf) => {
1859
+ },
1860
+ parseRegexNonEmpty = (buf: RegExBuf) => {
1840
1861
  match (parsePCEs(buf, false)) {
1841
1862
  Err(e) => Err(e),
1842
1863
  Ok(pces) => {
@@ -1922,7 +1943,7 @@ let rec isAnchored = (re: ParsedRegularExpression) => {
1922
1943
  REAlts(a, b) => isAnchored(a) && isAnchored(b),
1923
1944
  REConditional(_, rx1, rx2, _, _, _) =>
1924
1945
  isAnchored(rx1) &&
1925
- Option.mapWithDefault(isAnchored, false, rx2),
1946
+ Option.mapWithDefault(isAnchored, false, rx2),
1926
1947
  REGroup(rx, _) => isAnchored(rx),
1927
1948
  RECut(rx, _, _, _) => isAnchored(rx),
1928
1949
  _ => false,
@@ -2305,10 +2326,10 @@ let charMatcher = (toMatch, next_m) =>
2305
2326
  if (
2306
2327
  {
2307
2328
  pos < limit &&
2308
- match (matchBufChar(buf, pos)) {
2309
- Err(_) => false,
2310
- Ok(c) => toMatch == c,
2311
- }
2329
+ match (matchBufChar(buf, pos)) {
2330
+ Err(_) => false,
2331
+ Ok(c) => toMatch == c,
2332
+ }
2312
2333
  }
2313
2334
  ) next_m(buf, pos + 1, start, limit, end, state, stack) else None
2314
2335
  }
@@ -2326,10 +2347,10 @@ let charTailMatcher = toMatch =>
2326
2347
  if (
2327
2348
  {
2328
2349
  pos < limit &&
2329
- match (matchBufChar(buf, pos)) {
2330
- Err(_) => false,
2331
- Ok(c) => toMatch == c,
2332
- }
2350
+ match (matchBufChar(buf, pos)) {
2351
+ Err(_) => false,
2352
+ Ok(c) => toMatch == c,
2353
+ }
2333
2354
  }
2334
2355
  ) Some(pos + 1) else None
2335
2356
  }
@@ -2382,13 +2403,13 @@ let stringMatcher = (toMatch, len, next_m) =>
2382
2403
  if (
2383
2404
  {
2384
2405
  pos + len <= limit &&
2385
- subArraysEqual(
2386
- buf.matchInputExploded,
2387
- pos,
2388
- String.explode(toMatch),
2389
- 0,
2390
- len
2391
- )
2406
+ subArraysEqual(
2407
+ buf.matchInputExploded,
2408
+ pos,
2409
+ String.explode(toMatch),
2410
+ 0,
2411
+ len
2412
+ )
2392
2413
  }
2393
2414
  ) next_m(buf, pos + len, start, limit, end, state, stack) else None
2394
2415
  }
@@ -2406,13 +2427,13 @@ let stringTailMatcher = (toMatch, len) =>
2406
2427
  if (
2407
2428
  {
2408
2429
  pos + len <= limit &&
2409
- subArraysEqual(
2410
- buf.matchInputExploded,
2411
- pos,
2412
- String.explode(toMatch),
2413
- 0,
2414
- len
2415
- )
2430
+ subArraysEqual(
2431
+ buf.matchInputExploded,
2432
+ pos,
2433
+ String.explode(toMatch),
2434
+ 0,
2435
+ len
2436
+ )
2416
2437
  }
2417
2438
  ) Some(pos + len) else None
2418
2439
  }
@@ -2513,10 +2534,10 @@ let rangeMatcher = (rng: CharRange, next_m) =>
2513
2534
  if (
2514
2535
  {
2515
2536
  pos < limit &&
2516
- match (matchBufChar(buf, pos)) {
2517
- Err(_) => false,
2518
- Ok(c) => rangeContains(rng, Char.code(c)),
2519
- }
2537
+ match (matchBufChar(buf, pos)) {
2538
+ Err(_) => false,
2539
+ Ok(c) => rangeContains(rng, Char.code(c)),
2540
+ }
2520
2541
  }
2521
2542
  ) next_m(buf, pos + 1, start, limit, end, state, stack) else None
2522
2543
  }
@@ -2534,10 +2555,10 @@ let rangeTailMatcher = (rng: CharRange) =>
2534
2555
  if (
2535
2556
  {
2536
2557
  pos < limit &&
2537
- match (matchBufChar(buf, pos)) {
2538
- Err(_) => false,
2539
- Ok(c) => rangeContains(rng, Char.code(c)),
2540
- }
2558
+ match (matchBufChar(buf, pos)) {
2559
+ Err(_) => false,
2560
+ Ok(c) => rangeContains(rng, Char.code(c)),
2561
+ }
2541
2562
  }
2542
2563
  ) Some(pos + 1) else None
2543
2564
  }
@@ -2636,7 +2657,7 @@ let isWordChar = c => {
2636
2657
 
2637
2658
  let isWordBoundary = (buf, pos, start, limit, end) => {
2638
2659
  !((pos == start || !isWordChar(matchBufChar(buf, pos - 1))) ==
2639
- (pos == end || !isWordChar(matchBufChar(buf, pos))))
2660
+ (pos == end || !isWordChar(matchBufChar(buf, pos))))
2640
2661
  }
2641
2662
 
2642
2663
  let wordBoundaryMatcher = next_m =>
@@ -3608,7 +3629,7 @@ export record MatchResult {
3608
3629
  /**
3609
3630
  * Returns the contents of the given group
3610
3631
  */
3611
- group: Number -> Option<String>,
3632
+ group: Number -> Option<String>,
3612
3633
  /**
3613
3634
  * Returns the position of the given group
3614
3635
  */
@@ -3672,10 +3693,12 @@ let makeMatchResult = (origString, start, end, state) => {
3672
3693
  // Helpers for user-facing match functionality
3673
3694
 
3674
3695
  let fastDriveRegexIsMatch = (rx, string, startOffset, endOffset) => {
3675
- let state = if (rx.reReferences) Array.make(rx.reNumGroups, None)
3696
+ let state =
3697
+ if (rx.reReferences) Array.make(rx.reNumGroups, None)
3676
3698
  else Array.make(0, None)
3677
- let toWrap = if (startOffset == 0 && endOffset == String.length(string))
3678
- string else String.slice(startOffset, endOffset, string)
3699
+ let toWrap =
3700
+ if (startOffset == 0 && endOffset == String.length(string)) string
3701
+ else String.slice(startOffset, endOffset, string)
3679
3702
  let buf = makeMatchBuffer(toWrap)
3680
3703
  Option.isSome(
3681
3704
  searchMatch(rx, buf, 0, 0, Array.length(buf.matchInputExploded), state)
@@ -3687,8 +3710,9 @@ let rec fastDriveRegexMatchAll = (rx, string, startOffset, endOffset) => {
3687
3710
  []
3688
3711
  } else {
3689
3712
  let state = Array.make(rx.reNumGroups, None)
3690
- let toWrap = if (startOffset == 0 && endOffset == String.length(string))
3691
- string else String.slice(startOffset, endOffset, string)
3713
+ let toWrap =
3714
+ if (startOffset == 0 && endOffset == String.length(string)) string
3715
+ else String.slice(startOffset, endOffset, string)
3692
3716
  let buf = makeMatchBuffer(toWrap)
3693
3717
  match (searchMatch(
3694
3718
  rx,
@@ -3726,8 +3750,9 @@ let rec fastDriveRegexMatchAll = (rx, string, startOffset, endOffset) => {
3726
3750
 
3727
3751
  let fastDriveRegexMatch = (rx, string, startOffset, endOffset) => {
3728
3752
  let state = Array.make(rx.reNumGroups, None)
3729
- let toWrap = if (startOffset == 0 && endOffset == String.length(string))
3730
- string else String.slice(startOffset, endOffset, string)
3753
+ let toWrap =
3754
+ if (startOffset == 0 && endOffset == String.length(string)) string
3755
+ else String.slice(startOffset, endOffset, string)
3731
3756
  let buf = makeMatchBuffer(toWrap)
3732
3757
  match (searchMatch(
3733
3758
  rx,
@@ -3951,14 +3976,14 @@ let regexReplaceHelp =
3951
3976
  all: Bool,
3952
3977
  ) => {
3953
3978
  let buf = makeMatchBuffer(toSearch)
3954
- let mut out = []
3955
3979
  let rec loop = searchPos => {
3956
3980
  let state = Array.make(rx.reNumGroups, None)
3981
+ let inStart = max(0, searchPos - rx.reMaxLookbehind)
3957
3982
  let poss = searchMatch(
3958
3983
  rx,
3959
3984
  buf,
3960
3985
  searchPos,
3961
- searchPos,
3986
+ inStart,
3962
3987
  Array.length(buf.matchInputExploded),
3963
3988
  state
3964
3989
  )
@@ -4044,3 +4069,74 @@ export let replaceAll =
4044
4069
  ) => {
4045
4070
  regexReplaceHelp(rx, toSearch, replacement, true)
4046
4071
  }
4072
+
4073
+ let regexSplitHelp = (rx: RegularExpression, str: String, all: Bool) => {
4074
+ // Get list of matches
4075
+ let regexMatches = if (all) {
4076
+ findAll(rx, str)
4077
+ } else {
4078
+ match (find(rx, str)) {
4079
+ None => [],
4080
+ Some(m) => [m],
4081
+ }
4082
+ }
4083
+ // Perform replacements
4084
+ let mut out = []
4085
+ let mut currentLocation = 0
4086
+ List.forEach(regexMatch => {
4087
+ let locations = regexMatch.allGroupPositions()
4088
+ Array.forEachi((pos, i) => {
4089
+ match (pos) {
4090
+ Some((start, end)) => {
4091
+ if (i == 0) {
4092
+ // Add the string between this match and the last match
4093
+ out = [String.slice(currentLocation, start, str), ...out]
4094
+ } else {
4095
+ // This adds the groups back in
4096
+ out = [String.slice(start, end, str), ...out]
4097
+ }
4098
+ if (end > currentLocation) currentLocation = end
4099
+ },
4100
+ None => void,
4101
+ }
4102
+ }, locations)
4103
+ }, regexMatches)
4104
+ out = [String.slice(currentLocation, String.length(str), str), ...out]
4105
+ List.reverse(out)
4106
+ }
4107
+
4108
+ /**
4109
+ * Splits the given string at the first match for the given regular expression.
4110
+ *
4111
+ * If the regex pattern contains capture groups, the content of the groups
4112
+ * will be included in the output list.
4113
+ *
4114
+ * @param rx: The regular expression to match
4115
+ * @param str: The string to split
4116
+ * @returns A list of the split segments
4117
+ *
4118
+ * @example assert Regex.split(Result.unwrap(Regex.make(",")), "a,b,c") == [ "a", "b,c" ]
4119
+ *
4120
+ * @since v0.5.5
4121
+ */
4122
+ export let split = (rx: RegularExpression, str: String) => {
4123
+ regexSplitHelp(rx, str, false)
4124
+ }
4125
+
4126
+ /**
4127
+ * Splits the given string at every match for the given regular expression.
4128
+ *
4129
+ * If the regex pattern contains capture groups, the content of the groups
4130
+ * will be included in the output list.
4131
+ *
4132
+ * @param rx: The regular expression to match
4133
+ * @param str: The string to split
4134
+ * @returns A list of the split segments
4135
+ *
4136
+ * @example assert Regex.splitAll(Result.unwrap(Regex.make(",")), "a,b,c") == [ "a", "b", "c" ]
4137
+ *
4138
+ * @since v0.5.5
4139
+ */
4140
+ export let splitAll = (rx: RegularExpression, str: String) => {
4141
+ regexSplitHelp(rx, str, true)
4142
+ }