Skip to content

Commit 0c1eb73

Browse files
authored
feat(stdlib): Add split function to Regex module (#1469)
feat(stdlib): Add `splitAll` function to Regex module
1 parent 5222648 commit 0c1eb73

File tree

3 files changed

+180
-1
lines changed

3 files changed

+180
-1
lines changed

compiler/test/stdlib/regex.test.gr

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import {
1010
findAllRange,
1111
replace,
1212
replaceAll,
13+
split,
14+
splitAll,
1315
MatchResult,
1416
} from "regex"
1517

@@ -765,3 +767,40 @@ assert replaceAll(unwrapResult(make("b(ar)")), "bazbarfoo", "$`") == "bazbazfoo"
765767
assert replaceAll(unwrapResult(make("b(ar)")), "bazbarfoo", "$'") == "bazfoofoo"
766768
// https://github.com/grain-lang/grain/issues/1431
767769
assert replaceAll(unwrapResult(make("^.")), "asdf", "-") == "-sdf"
770+
771+
// Regex.split
772+
assert split(unwrapResult(make(",")), "a,b,c,d") == ["a", "b,c,d"]
773+
assert split(unwrapResult(make("|")), "a,b,c,d") == ["a,b,c,d"]
774+
assert split(unwrapResult(make("\n|\r")), "a\nb\nc\rd\n\re") ==
775+
["a", "b\nc\rd\n\re"]
776+
assert split(unwrapResult(make(".")), "abcd") == ["", "bcd"]
777+
assert split(unwrapResult(make("d$")), "abcd") == ["abc", ""]
778+
assert split(unwrapResult(make("b(ar)")), "foo bar") == ["foo ", "ar", ""]
779+
assert split(unwrapResult(make("b(ar)")), "foo bar bar") ==
780+
["foo ", "ar", " bar"]
781+
assert split(unwrapResult(make("b(ar)b(az)")), "foo barbaz bar") ==
782+
["foo ", "ar", "az", " bar"]
783+
assert split(unwrapResult(make("b((a)r)")), "bar") == ["", "ar", "a", ""]
784+
assert split(unwrapResult(make("b(((((a))))r)")), "bar") ==
785+
["", "ar", "a", "a", "a", "a", ""]
786+
assert split(unwrapResult(make("b(((((a))))r)")), "bar bar") ==
787+
["", "ar", "a", "a", "a", "a", " bar"]
788+
// Regex.splitAll
789+
assert splitAll(unwrapResult(make(",")), "a,b,c,d") == ["a", "b", "c", "d"]
790+
assert splitAll(unwrapResult(make("|")), "a,b,c,d") == ["a,b,c,d"]
791+
assert splitAll(unwrapResult(make("\n|\r")), "a\nb\nc\rd\n\re") ==
792+
["a", "b", "c", "d", "", "e"]
793+
assert splitAll(unwrapResult(make(".")), "abcd") == ["", "", "", "", ""]
794+
assert splitAll(unwrapResult(make("d$")), "abcd") == ["abc", ""]
795+
assert splitAll(unwrapResult(make("b(ar)(ar)")), "foo barar test") ==
796+
["foo ", "ar", "ar", " test"]
797+
assert splitAll(
798+
unwrapResult(make("b(ar)(ar)")),
799+
"foo barar test barar test2"
800+
) ==
801+
["foo ", "ar", "ar", " test ", "ar", "ar", " test2"]
802+
assert splitAll(unwrapResult(make("b((a)r)")), "bar") == ["", "ar", "a", ""]
803+
assert splitAll(unwrapResult(make("b(((((a))))r)")), "bar") ==
804+
["", "ar", "a", "a", "a", "a", ""]
805+
assert splitAll(unwrapResult(make("b(((((a))))r)")), "bar bar") ==
806+
["", "ar", "a", "a", "a", "a", " ", "ar", "a", "a", "a", "a", ""]

stdlib/regex.gr

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3973,7 +3973,6 @@ let regexReplaceHelp =
39733973
all: Bool,
39743974
) => {
39753975
let buf = makeMatchBuffer(toSearch)
3976-
let mut out = []
39773976
let rec loop = searchPos => {
39783977
let state = Array.make(rx.reNumGroups, None)
39793978
let inStart = max(0, searchPos - rx.reMaxLookbehind)
@@ -4067,3 +4066,74 @@ export let replaceAll =
40674066
) => {
40684067
regexReplaceHelp(rx, toSearch, replacement, true)
40694068
}
4069+
4070+
let regexSplitHelp = (rx: RegularExpression, str: String, all: Bool) => {
4071+
// Get list of matches
4072+
let regexMatches = if (all) {
4073+
findAll(rx, str)
4074+
} else {
4075+
match (find(rx, str)) {
4076+
None => [],
4077+
Some(m) => [m],
4078+
}
4079+
}
4080+
// Perform replacements
4081+
let mut out = []
4082+
let mut currentLocation = 0
4083+
List.forEach(regexMatch => {
4084+
let locations = regexMatch.allGroupPositions()
4085+
Array.forEachi((pos, i) => {
4086+
match (pos) {
4087+
Some((start, end)) => {
4088+
if (i == 0) {
4089+
// Add the string between this match and the last match
4090+
out = [String.slice(currentLocation, start, str), ...out]
4091+
} else {
4092+
// This adds the groups back in
4093+
out = [String.slice(start, end, str), ...out]
4094+
}
4095+
if (end > currentLocation) currentLocation = end
4096+
},
4097+
None => void,
4098+
}
4099+
}, locations)
4100+
}, regexMatches)
4101+
out = [String.slice(currentLocation, String.length(str), str), ...out]
4102+
List.reverse(out)
4103+
}
4104+
4105+
/**
4106+
* Splits the given string at the first match for the given regular expression.
4107+
*
4108+
* If the regex pattern contains capture groups, the content of the groups
4109+
* will be included in the output list.
4110+
*
4111+
* @param rx: The regular expression to match
4112+
* @param str: The string to split
4113+
* @returns A list of the split segments
4114+
*
4115+
* @example assert Regex.split(Result.unwrap(Regex.make(",")), "a,b,c") == [ "a", "b,c" ]
4116+
*
4117+
* @since v0.5.5
4118+
*/
4119+
export let split = (rx: RegularExpression, str: String) => {
4120+
regexSplitHelp(rx, str, false)
4121+
}
4122+
4123+
/**
4124+
* Splits the given string at every match for the given regular expression.
4125+
*
4126+
* If the regex pattern contains capture groups, the content of the groups
4127+
* will be included in the output list.
4128+
*
4129+
* @param rx: The regular expression to match
4130+
* @param str: The string to split
4131+
* @returns A list of the split segments
4132+
*
4133+
* @example assert Regex.splitAll(Result.unwrap(Regex.make(",")), "a,b,c") == [ "a", "b", "c" ]
4134+
*
4135+
* @since v0.5.5
4136+
*/
4137+
export let splitAll = (rx: RegularExpression, str: String) => {
4138+
regexSplitHelp(rx, str, true)
4139+
}

stdlib/regex.md

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,3 +447,73 @@ Examples:
447447
assert Regex.replaceAll(Result.unwrap(Regex.make("o")), "skoot", "r") == "skrrt"
448448
```
449449

450+
### Regex.**split**
451+
452+
<details disabled>
453+
<summary tabindex="-1">Added in <code>next</code></summary>
454+
No other changes yet.
455+
</details>
456+
457+
```grain
458+
split : (RegularExpression, String) -> List<String>
459+
```
460+
461+
Splits the given string at the first match for the given regular expression.
462+
463+
If the regex pattern contains capture groups, the content of the groups
464+
will be included in the output list.
465+
466+
Parameters:
467+
468+
|param|type|description|
469+
|-----|----|-----------|
470+
|`rx`|`RegularExpression`|The regular expression to match|
471+
|`str`|`String`|The string to split|
472+
473+
Returns:
474+
475+
|type|description|
476+
|----|-----------|
477+
|`List<String>`|A list of the split segments|
478+
479+
Examples:
480+
481+
```grain
482+
assert Regex.split(Result.unwrap(Regex.make(",")), "a,b,c") == [ "a", "b,c" ]
483+
```
484+
485+
### Regex.**splitAll**
486+
487+
<details disabled>
488+
<summary tabindex="-1">Added in <code>next</code></summary>
489+
No other changes yet.
490+
</details>
491+
492+
```grain
493+
splitAll : (RegularExpression, String) -> List<String>
494+
```
495+
496+
Splits the given string at every match for the given regular expression.
497+
498+
If the regex pattern contains capture groups, the content of the groups
499+
will be included in the output list.
500+
501+
Parameters:
502+
503+
|param|type|description|
504+
|-----|----|-----------|
505+
|`rx`|`RegularExpression`|The regular expression to match|
506+
|`str`|`String`|The string to split|
507+
508+
Returns:
509+
510+
|type|description|
511+
|----|-----------|
512+
|`List<String>`|A list of the split segments|
513+
514+
Examples:
515+
516+
```grain
517+
assert Regex.splitAll(Result.unwrap(Regex.make(",")), "a,b,c") == [ "a", "b", "c" ]
518+
```
519+

0 commit comments

Comments
 (0)