rregex 1.10.1 → 1.10.2
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/README.md +6 -294
- package/lib/browser.d.ts +163 -17
- package/lib/browser.js +80 -11
- package/lib/commonjs.d.ts +163 -17
- package/lib/commonjs.js +80 -11
- package/lib/module.d.ts +163 -17
- package/lib/module.js +80 -11
- package/lib/types.d.ts +163 -17
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,23 +6,12 @@ A WebAssembly build of [Rust Regex](https://docs.rs/regex/latest/regex/) for Jav
|
|
|
6
6
|
|
|
7
7
|
- [Why Rust Regex](#why-rust-regex)
|
|
8
8
|
- [Install](#install)
|
|
9
|
-
- [API](
|
|
10
|
-
- [`
|
|
11
|
-
- [`
|
|
12
|
-
- [`
|
|
13
|
-
- [`
|
|
14
|
-
- [`
|
|
15
|
-
- [`capturesLength(): number;`](#captureslength-number) (since 1.8)
|
|
16
|
-
- [`captureNames(): string[];`](#capturenames-string) (since 1.8)
|
|
17
|
-
- [`captures(text: string): Captures | undefined;`](#capturestext-string-captures--undefined) (since 1.8)
|
|
18
|
-
- [`capturesAll(text: string): Captures[];`](#capturesalltext-string-captures) (since 1.8)
|
|
19
|
-
- [`replace(text: string, rep: string): string;`](#replacetext-string-rep-string-string)
|
|
20
|
-
- [`replaceAll(text: string, rep: string): string;`](#replacealltext-string-rep-string-string)
|
|
21
|
-
- [`replacen(text: string, limit: number, rep: string): string;`](#replacentext-string-limit-number-rep-string-string)
|
|
22
|
-
- [`split(text: string): string[];`](#splittext-string-string)
|
|
23
|
-
- [`splitn(text: string, limit: number): string[];`](#splitntext-string-limit-number-string)
|
|
24
|
-
- [`shortestMatch(text: string): number | undefined;`](#shortestmatchtext-string-number--undefined)
|
|
25
|
-
- [`shortestMatchAt(text: string, limit: number): number | undefined;`](#shortestmatchattext-string-limit-number-number--undefined)
|
|
9
|
+
- [API](https://tsdocs.dev/docs/rregex/latest)
|
|
10
|
+
- [`RRegex`](https://tsdocs.dev/docs/rregex/latest/classes/RRegex.html)
|
|
11
|
+
- [`RRegexSet`](https://tsdocs.dev/docs/rregex/latest/classes/RRegexSet.html)
|
|
12
|
+
- [`Match`](https://tsdocs.dev/docs/rregex/latest/types/Match.html)
|
|
13
|
+
- [`Captures`](https://tsdocs.dev/docs/rregex/latest/types/Captures.html)
|
|
14
|
+
- [`escape`](https://tsdocs.dev/docs/rregex/latest/functions/escape.html)
|
|
26
15
|
- [Known Issues](#known-issues)
|
|
27
16
|
|
|
28
17
|
## Why Rust Regex
|
|
@@ -37,283 +26,6 @@ Rust has a powerful Regex library with a lot of features that don't exists en th
|
|
|
37
26
|
npm install rregex
|
|
38
27
|
```
|
|
39
28
|
|
|
40
|
-
## API
|
|
41
|
-
|
|
42
|
-
Similar to the native `Regex` object you can create a new `RRegex` instance using a string.
|
|
43
|
-
|
|
44
|
-
```typescript
|
|
45
|
-
import { RRegex } from "rregex";
|
|
46
|
-
const re = new RRegex("^d{4}-d{2}-d{2}$");
|
|
47
|
-
assert.equal(re.isMatch("2014-01-01"), true);
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
> Note: It doesn't take a second parameter because fags are part of the syntax ([See Documentation](https://docs.rs/regex/latest/regex/#grouping-and-flags))
|
|
51
|
-
|
|
52
|
-
### `isMatch(text: string): boolean;`
|
|
53
|
-
|
|
54
|
-
Returns true if and only if there is a match for the regex in the string given.
|
|
55
|
-
|
|
56
|
-
```typescript
|
|
57
|
-
const text = "I categorically deny having triskaidekaphobia.";
|
|
58
|
-
const re = new RRegex("\\b\\w{13}\\b");
|
|
59
|
-
expect(re.isMatch(text)).toEqual(true);
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
[See Documentation for `is_match`](https://docs.rs/regex/latest/regex/struct.Regex.html#method.is_match)
|
|
63
|
-
|
|
64
|
-
### `isMatchAt(text: string, limit: number): boolean;`
|
|
65
|
-
|
|
66
|
-
Returns the same as is_match, but starts the search at the given offset.
|
|
67
|
-
|
|
68
|
-
[See Documentation for `is_match_at`](https://docs.rs/regex/latest/regex/struct.Regex.html#method.is_match_at)
|
|
69
|
-
|
|
70
|
-
```typescript
|
|
71
|
-
const text = "I categorically deny having triskaidekaphobia.";
|
|
72
|
-
const re = new RRegex("\\b\\w{13}\\b");
|
|
73
|
-
expect(re.isMatchAt(text, 1)).toBe(true);
|
|
74
|
-
expect(re.isMatchAt(text, 5)).toBe(false);
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
### `find(text: string): Match | undefined;`
|
|
78
|
-
|
|
79
|
-
Returns the start and end byte range of the leftmost-first match in `text`. If no match exists, then `undefined` is returned.
|
|
80
|
-
|
|
81
|
-
```typescript
|
|
82
|
-
const text = "I categorically deny having triskaidekaphobia.";
|
|
83
|
-
const re = new RRegex("\\b\\w{13}\\b");
|
|
84
|
-
expect(re.find(text)).toEqual({
|
|
85
|
-
value: "categorically",
|
|
86
|
-
start: 2,
|
|
87
|
-
end: 15,
|
|
88
|
-
});
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
[See Documentation for `find`](https://docs.rs/regex/latest/regex/struct.Regex.html#method.find)
|
|
92
|
-
|
|
93
|
-
### `findAt(text: string): Match | undefined;`
|
|
94
|
-
|
|
95
|
-
Returns the same as find, but starts the search at the given offset.
|
|
96
|
-
|
|
97
|
-
```typescript
|
|
98
|
-
const text = "I categorically deny having triskaidekaphobia.";
|
|
99
|
-
const re = new RRegex("\\b\\w{13}\\b");
|
|
100
|
-
expect(re.findAt(text, 1)).toEqual({
|
|
101
|
-
value: "categorically",
|
|
102
|
-
start: 2,
|
|
103
|
-
end: 15,
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
expect(re.findAt(text, 5)).toEqual(undefined);
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
([See Documentation for `find_at`](https://docs.rs/regex/latest/regex/struct.Regex.html#method.find_at))
|
|
110
|
-
|
|
111
|
-
### `findAll(text: string): Match[];`
|
|
112
|
-
|
|
113
|
-
Returns an array for each successive non-overlapping match in text, returning the start and end byte indices with respect to text
|
|
114
|
-
|
|
115
|
-
```typescript
|
|
116
|
-
const text = 'Retroactively relinquishing remunerations is reprehensible.'
|
|
117
|
-
const re = new RRegex('\\b\\w{13}\\b')
|
|
118
|
-
expect(re.findAll(text)).toEqual([
|
|
119
|
-
{
|
|
120
|
-
"end": 13,
|
|
121
|
-
"start": 0,
|
|
122
|
-
"value": "Retroactively",
|
|
123
|
-
},
|
|
124
|
-
{
|
|
125
|
-
"end": 27,
|
|
126
|
-
"start": 14,
|
|
127
|
-
"value": "relinquishing",
|
|
128
|
-
},
|
|
129
|
-
{
|
|
130
|
-
"end": 41,
|
|
131
|
-
"start": 28,
|
|
132
|
-
"value": "remunerations",
|
|
133
|
-
},
|
|
134
|
-
{
|
|
135
|
-
"end": 58,
|
|
136
|
-
"start": 45,
|
|
137
|
-
"value": "reprehensible",
|
|
138
|
-
},
|
|
139
|
-
])
|
|
140
|
-
})
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
[See Documentation for `find_iter`](https://docs.rs/regex/latest/regex/struct.Regex.html#method.find_iter)
|
|
144
|
-
|
|
145
|
-
### `captureNames(): string[];`
|
|
146
|
-
|
|
147
|
-
> Note: available since 1.8
|
|
148
|
-
|
|
149
|
-
Returns a list of the capture names in this regex.
|
|
150
|
-
|
|
151
|
-
[See Documentation for `capture_names`](https://docs.rs/regex/latest/regex/struct.Regex.html#method.capture_names)
|
|
152
|
-
|
|
153
|
-
### `captures(text: string): Captures | undefined;`
|
|
154
|
-
|
|
155
|
-
> Note: available since 1.8
|
|
156
|
-
|
|
157
|
-
Returns the capture groups corresponding to the leftmost-first
|
|
158
|
-
match in `text`. Capture group `0` always corresponds to the entire
|
|
159
|
-
match. If no match is found, then `undefined` is returned.
|
|
160
|
-
|
|
161
|
-
```typescript
|
|
162
|
-
const re = new Regex("'([^']+)'\\s+\\((\\d{4})\)")
|
|
163
|
-
const text = "Not my favorite movie: 'Citizen Kane' (1941)."
|
|
164
|
-
const caps = re.captures(text)
|
|
165
|
-
expect(caps.get[1].value).toBe("Citizen Kane")
|
|
166
|
-
expect(caps.get[2].value).toBe("1941")
|
|
167
|
-
expect(caps.get[0].value).toBe("'Citizen Kane' (1941)")
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
### `capturesAll(text: string): Captures[];`
|
|
171
|
-
|
|
172
|
-
> Note: available since 1.8
|
|
173
|
-
|
|
174
|
-
Returns a list with all the non-overlapping capture groups matched
|
|
175
|
-
in `text`. This is operationally the same as `findAll`, except it
|
|
176
|
-
returns information about capturing group matches.
|
|
177
|
-
|
|
178
|
-
```typescript
|
|
179
|
-
const re = new Regex("'(?P<title>[^']+)'\\s+\\((?P<year>\\d{4})\\)")
|
|
180
|
-
const text = "'Citizen Kane' (1941), 'The Wizard of Oz' (1939), 'M' (1931)."
|
|
181
|
-
for caps of re.captures_iter(text) {
|
|
182
|
-
console.log(
|
|
183
|
-
"Movie:", caps.name["title"].value, ","
|
|
184
|
-
"Released:", caps.name["year"].value
|
|
185
|
-
);
|
|
186
|
-
}
|
|
187
|
-
// Output:
|
|
188
|
-
// Movie: Citizen Kane, Released: 1941
|
|
189
|
-
// Movie: The Wizard of Oz, Released: 1939
|
|
190
|
-
// Movie: M, Released: 1931
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
[See the documentation for `captures_iter`](https://docs.rs/regex/latest/regex/struct.Regex.html#method.captures_iter))
|
|
194
|
-
|
|
195
|
-
### `capturesLength(): number;`
|
|
196
|
-
|
|
197
|
-
> Note: available since 1.8
|
|
198
|
-
|
|
199
|
-
Returns the number of captures.
|
|
200
|
-
|
|
201
|
-
This includes all named and unnamed groups, including the implicit
|
|
202
|
-
unnamed group that is always present and corresponds to the entire match.
|
|
203
|
-
|
|
204
|
-
Since the implicit unnamed group is always included in this length, the
|
|
205
|
-
length returned is guaranteed to be greater than zero.
|
|
206
|
-
|
|
207
|
-
```typescript
|
|
208
|
-
const re1 = new RRegex("(?P<y>\\d{4})-(?P<m>\\d{2})-(?P<d>\\d{2})");
|
|
209
|
-
expect(re1.capturesLength()).toEqual(4);
|
|
210
|
-
|
|
211
|
-
const re2 = new RRegex("foo");
|
|
212
|
-
expect(re2.capturesLength()).toEqual(1);
|
|
213
|
-
|
|
214
|
-
const re3 = new RRegex("(foo)");
|
|
215
|
-
expect(re3.capturesLength()).toEqual(2);
|
|
216
|
-
|
|
217
|
-
const re4 = new RRegex("(?<a>.(?<b>.))(.)(?:.)(?<c>.)");
|
|
218
|
-
expect(re4.capturesLength()).toEqual(5);
|
|
219
|
-
|
|
220
|
-
const re5 = new RRegex("[a&&b]");
|
|
221
|
-
expect(re5.capturesLength()).toEqual(1);
|
|
222
|
-
```
|
|
223
|
-
|
|
224
|
-
[See the documentation for `captures_len`](https://docs.rs/regex/latest/regex/struct.Regex.html#method.captures_len)
|
|
225
|
-
|
|
226
|
-
### `replace(text: string, rep: string): string;`
|
|
227
|
-
|
|
228
|
-
Replaces the leftmost-first match with the replacement provided.
|
|
229
|
-
|
|
230
|
-
If no match is found, then a copy of the string is returned unchanged.
|
|
231
|
-
|
|
232
|
-
Note that this function is polymorphic with respect to the replacement. In typical usage, this can just be a normal string:
|
|
233
|
-
|
|
234
|
-
```typescript
|
|
235
|
-
const re = new RRegex("[^01]+");
|
|
236
|
-
expect(re.replace("1078910", "")).toBe("1010");
|
|
237
|
-
```
|
|
238
|
-
|
|
239
|
-
But this is a bit cumbersome to use all the time. Instead, a simple syntax is supported that expands `$name` into the corresponding capture group. Here’s the last example, but using this expansion technique with named capture groups:
|
|
240
|
-
|
|
241
|
-
```typescript
|
|
242
|
-
const re = new RRegex("(?P<last>[^,\\s]+),\\s+(?P<first>\\S+)");
|
|
243
|
-
const result = re.replace("Springsteen, Bruce", "$first $last");
|
|
244
|
-
expect(result).toEqual("Bruce Springsteen");
|
|
245
|
-
```
|
|
246
|
-
|
|
247
|
-
Note that using `$2` instead of `$first` or `$1` instead of `$last` would produce the same result. To write a literal `$` use `$$`.
|
|
248
|
-
|
|
249
|
-
Sometimes the replacement string requires use of curly braces to delineate a capture group replacement and surrounding literal text. For example, if we wanted to join two words together with an underscore:
|
|
250
|
-
|
|
251
|
-
```typescript
|
|
252
|
-
const re = new RRegex("(?P<first>\\w+)\\s+(?P<second>\\w+)");
|
|
253
|
-
const result = re.replace("deep fried", "${first}_$second");
|
|
254
|
-
expect(result).toEqual("deep_fried");
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
Without the curly braces, the capture group name `first_` would be used, and since it doesn’t exist, it would be replaced with the empty string.
|
|
258
|
-
|
|
259
|
-
[See Documentation for `replace`](https://docs.rs/regex/latest/regex/struct.Regex.html#method.replace)
|
|
260
|
-
|
|
261
|
-
### `replaceAll(text: string, rep: string): string;`
|
|
262
|
-
|
|
263
|
-
Replaces all non-overlapping matches in `text` with the replacement provided. This is the same as calling `replacen` with `limit` set to `0`.
|
|
264
|
-
|
|
265
|
-
[See the documentation for `replace_all`](https://docs.rs/regex/latest/regex/struct.Regex.html#method.replace_all)
|
|
266
|
-
|
|
267
|
-
### `replacen(text: string, limit: number, rep: string): string;`
|
|
268
|
-
|
|
269
|
-
Replaces at most `limit` non-overlapping matches in `text` with the replacement provided. If `limit` is 0, then all non-overlapping matches are replaced.
|
|
270
|
-
|
|
271
|
-
[See Documentation for `replacen`](https://docs.rs/regex/latest/regex/struct.Regex.html#method.replacen)
|
|
272
|
-
|
|
273
|
-
### `split(text: string): string[];`
|
|
274
|
-
|
|
275
|
-
Returns an iterator of substrings of `text` delimited by a match of the regular expression. Namely, each element of the iterator corresponds to text that isn’t matched by the regular expression.
|
|
276
|
-
|
|
277
|
-
```typescript
|
|
278
|
-
const re = new RRegex("[ \\t]+");
|
|
279
|
-
const fields = re.split("a b \t c\td e");
|
|
280
|
-
expect(fields).toEqual(["a", "b", "c", "d", "e"]);
|
|
281
|
-
```
|
|
282
|
-
|
|
283
|
-
[See Documentation for `split`](https://docs.rs/regex/latest/regex/struct.Regex.html#method.split)
|
|
284
|
-
|
|
285
|
-
### `splitn(text: string, limit: number): string[];`
|
|
286
|
-
|
|
287
|
-
Returns an iterator of at most `limit` substrings of `text` delimited by a match of the regular expression. (A limit of 0 will return no substrings.) Namely, each element of the iterator corresponds to text that isn’t matched by the regular expression. The remainder of the string that is not split will be the last element in the iterator.
|
|
288
|
-
|
|
289
|
-
```typescript
|
|
290
|
-
const re = new RRegex("\\W+");
|
|
291
|
-
const fields = re.splitn("Hey! How are you?", 3);
|
|
292
|
-
expect(fields).toEqual(["Hey", "How", "are you?"]);
|
|
293
|
-
```
|
|
294
|
-
|
|
295
|
-
[See Documentation for `splitn`](https://docs.rs/regex/latest/regex/struct.Regex.html#method.splitn)
|
|
296
|
-
|
|
297
|
-
### `shortestMatch(text: string): number | undefined;`
|
|
298
|
-
|
|
299
|
-
Returns the end location of a match in the text given.
|
|
300
|
-
|
|
301
|
-
This method may have the same performance characteristics as `is_match`, except it provides an end location for a match. In particular, the location returned may be shorter than the proper end of the leftmost-first match.
|
|
302
|
-
|
|
303
|
-
```typescript
|
|
304
|
-
const text = "aaaaa";
|
|
305
|
-
const pos = new RRegex("a+");
|
|
306
|
-
expect(pos.shortestMatch(text)).toBe(1);
|
|
307
|
-
```
|
|
308
|
-
|
|
309
|
-
[See Documentation for `shortest_match`](https://docs.rs/regex/latest/regex/struct.Regex.html#method.shortest_match)
|
|
310
|
-
|
|
311
|
-
### `shortestMatchAt(text: string, limit: number): number | undefined;`
|
|
312
|
-
|
|
313
|
-
Returns the same as shortest_match, but starts the search at the given offset.
|
|
314
|
-
|
|
315
|
-
[See Documentation for `shortest_match_at`](https://docs.rs/regex/latest/regex/struct.Regex.html#method.shortest_match_at)
|
|
316
|
-
|
|
317
29
|
## Known Issues
|
|
318
30
|
|
|
319
31
|
If you call `splitn(text, limit)` and the expected result length is equal to `limit - 1` the result will include an extra item `""`, this behavior does not happen if `limit` es greater. **`fixed at >=1.3`**
|
package/lib/browser.d.ts
CHANGED
|
@@ -11,6 +11,50 @@
|
|
|
11
11
|
export function escape(text: string): string;
|
|
12
12
|
/**
|
|
13
13
|
* Represents a single match of a regex in a haystack.
|
|
14
|
+
*
|
|
15
|
+
* A `Match` contains both the start and end byte offsets of the match and the
|
|
16
|
+
* actual substring corresponding to the range of those byte offsets. It is
|
|
17
|
+
* guaranteed that `start <= end`. When `start == end`, the match is empty.
|
|
18
|
+
*
|
|
19
|
+
* Since this `Match` can only be produced by the top-level `Regex` APIs
|
|
20
|
+
* that only support searching UTF-8 encoded strings, the byte offsets for a
|
|
21
|
+
* `Match` are guaranteed to fall on valid UTF-8 codepoint boundaries. That
|
|
22
|
+
* is, slicing an `Uint8Array` created with a TextEncoder is guaranteed to never
|
|
23
|
+
* be out of range.
|
|
24
|
+
*
|
|
25
|
+
* # Numbering
|
|
26
|
+
*
|
|
27
|
+
* The byte offsets in a `Match` form a half-open interval. That is, the
|
|
28
|
+
* start of the range is inclusive and the end of the range is exclusive.
|
|
29
|
+
* For example, given a haystack `abcFOOxyz` and a match of `FOO`, its byte
|
|
30
|
+
* offset range starts at `3` and ends at `6`. `3` corresponds to `F` and
|
|
31
|
+
* `6` corresponds to `x`, which is one past the end of the match. This
|
|
32
|
+
* corresponds to the same kind of slicing that Rust uses.
|
|
33
|
+
*
|
|
34
|
+
* For more on why this was chosen over other schemes (aside from being
|
|
35
|
+
* consistent with how Rust the language works), see [this discussion] and
|
|
36
|
+
* [Dijkstra's note on a related topic][note].
|
|
37
|
+
*
|
|
38
|
+
* [this discussion]: https://github.com/rust-lang/regex/discussions/866
|
|
39
|
+
* [note]: https://www.cs.utexas.edu/users/EWD/transcriptions/EWD08xx/EWD831.html
|
|
40
|
+
*
|
|
41
|
+
* # Example
|
|
42
|
+
*
|
|
43
|
+
* This example shows the value of each of the methods on `Match` for a
|
|
44
|
+
* particular search.
|
|
45
|
+
*
|
|
46
|
+
* ```typescript
|
|
47
|
+
* import { RRegex } from "rregex"
|
|
48
|
+
*
|
|
49
|
+
* const re = new RRegex("\\p{Greek}+");
|
|
50
|
+
* const hay = "Greek: αβγδ";
|
|
51
|
+
* const m = re.find(hay);
|
|
52
|
+
* expect(m.start).toBe(7);
|
|
53
|
+
* expect(m.end).toBe(15);
|
|
54
|
+
* expect(m.value).toBe("αβγδ");
|
|
55
|
+
* ```
|
|
56
|
+
*
|
|
57
|
+
* @see @see https://docs.rs/regex/latest/regex/
|
|
14
58
|
*/
|
|
15
59
|
export type Match = {
|
|
16
60
|
start: number
|
|
@@ -19,14 +63,47 @@ export type Match = {
|
|
|
19
63
|
}
|
|
20
64
|
|
|
21
65
|
/**
|
|
22
|
-
*
|
|
66
|
+
* Represents the capture groups for a single match.
|
|
67
|
+
*
|
|
68
|
+
* Capture groups refer to parts of a regex enclosed in parentheses. They can
|
|
69
|
+
* be optionally named. The purpose of capture groups is to be able to
|
|
70
|
+
* reference different parts of a match based on the original pattern. For
|
|
71
|
+
* example, say you want to match the individual letters in a 5-letter word:
|
|
23
72
|
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* 0th capture is always unnamed and so must be accessed with the get property.)
|
|
73
|
+
* ```text
|
|
74
|
+
* (?<first>\w)(\w)(?:\w)\w(?<last>\w)
|
|
75
|
+
* ```
|
|
28
76
|
*
|
|
29
|
-
*
|
|
77
|
+
* This regex has 4 capture groups:
|
|
78
|
+
*
|
|
79
|
+
* * The group at index `0` corresponds to the overall match. It is always
|
|
80
|
+
* present in every match and never has a name.
|
|
81
|
+
* * The group at index `1` with name `first` corresponding to the first
|
|
82
|
+
* letter.
|
|
83
|
+
* * The group at index `2` with no name corresponding to the second letter.
|
|
84
|
+
* * The group at index `3` with name `last` corresponding to the fifth and
|
|
85
|
+
* last letter.
|
|
86
|
+
*
|
|
87
|
+
* Notice that `(?:\w)` was not listed above as a capture group despite it
|
|
88
|
+
* being enclosed in parentheses. That's because `(?:pattern)` is a special
|
|
89
|
+
* syntax that permits grouping but *without* capturing. The reason for not
|
|
90
|
+
* treating it as a capture is that tracking and reporting capture groups
|
|
91
|
+
* requires additional state that may lead to slower searches. So using as few
|
|
92
|
+
* capture groups as possible can help performance. (Although the difference
|
|
93
|
+
* in performance of a couple of capture groups is likely immaterial.)
|
|
94
|
+
*
|
|
95
|
+
* # Example
|
|
96
|
+
*
|
|
97
|
+
* ```typescript
|
|
98
|
+
* import { RRegex } from "rregex"
|
|
99
|
+
*
|
|
100
|
+
* const re = new RRegex("(?<first>\\w)(\\w)(?:\\w)\\w(?<last>\\w)");
|
|
101
|
+
* const caps = re.captures("toady");
|
|
102
|
+
* expect(caps.get[0].value).toBe("toady");
|
|
103
|
+
* expect(caps.name["first"].value).toBe("t");
|
|
104
|
+
* expect(caps.get[2].value).toBe("o");
|
|
105
|
+
* expect(caps.name["last"].value).toBe("y");
|
|
106
|
+
* ```
|
|
30
107
|
*/
|
|
31
108
|
export type Captures = {
|
|
32
109
|
get: Match[]
|
|
@@ -584,6 +661,43 @@ export type Capture = {
|
|
|
584
661
|
|
|
585
662
|
/**
|
|
586
663
|
* A compiled regular expression for matching Unicode strings.
|
|
664
|
+
*
|
|
665
|
+
* A `RRegex` can be used to search haystacks, split haystacks into substrings
|
|
666
|
+
* or replace substrings in a haystack with a different substring. All
|
|
667
|
+
* searching is done with an implicit `(?s:.)*?` at the beginning and end of
|
|
668
|
+
* an pattern. To force an expression to match the whole string (or a prefix
|
|
669
|
+
* or a suffix), you must use an anchor like `^` or `$` (or `\A` and `\z`).
|
|
670
|
+
*
|
|
671
|
+
* While this library will handle Unicode strings (whether in the regular
|
|
672
|
+
* expression or in the haystack), all positions returned are **byte
|
|
673
|
+
* offsets**. Every byte offset is guaranteed to be at a Unicode code point
|
|
674
|
+
* boundary. That is, all offsets returned by the `RRegex` API are guaranteed
|
|
675
|
+
* to be ranges that can slice an `Uint8Array` created with a TextEncoder.
|
|
676
|
+
*
|
|
677
|
+
* # Example: slicing over UTF strings
|
|
678
|
+
*
|
|
679
|
+
* ```typescript
|
|
680
|
+
* import { RRegex } from "rregex"
|
|
681
|
+
*
|
|
682
|
+
* const re = new RRegex("ä")
|
|
683
|
+
* const m = re.find("äöü") // { start: 0, end: 2 }
|
|
684
|
+
*
|
|
685
|
+
* const buff = new TextEncoder().encode("äöü")
|
|
686
|
+
* const slice = buff.slice(m.start, m.end)
|
|
687
|
+
* expect(new TextDecoder().decode(slice)).toBe("ä")
|
|
688
|
+
* ```
|
|
689
|
+
*
|
|
690
|
+
* # Example: find the offsets of a US phone number:
|
|
691
|
+
*
|
|
692
|
+
* ```typescript
|
|
693
|
+
* import { RRegex } from "rregex"
|
|
694
|
+
*
|
|
695
|
+
* const re = new RRegex("[0-9]{3}-[0-9]{3}-[0-9]{4}");
|
|
696
|
+
* const m = re.find("phone: 111-222-3333");
|
|
697
|
+
* expect(m.start).toBe(7);
|
|
698
|
+
* expect(m.end).toBe(19);
|
|
699
|
+
* ```
|
|
700
|
+
*
|
|
587
701
|
* @see https://docs.rs/regex/latest/regex/
|
|
588
702
|
*/
|
|
589
703
|
export class RRegex {
|
|
@@ -610,6 +724,8 @@ export class RRegex {
|
|
|
610
724
|
* Unicode word characters:
|
|
611
725
|
*
|
|
612
726
|
* ```typescript
|
|
727
|
+
* import { RRegex } from "rregex"
|
|
728
|
+
*
|
|
613
729
|
* const text = "I categorically deny having triskaidekaphobia."
|
|
614
730
|
* expect(new RRegex("\\b\\w{13}\\b").is_match(text)).toBe(true)
|
|
615
731
|
* ```
|
|
@@ -645,8 +761,10 @@ export class RRegex {
|
|
|
645
761
|
* Unicode word characters:
|
|
646
762
|
*
|
|
647
763
|
* ```typescript
|
|
764
|
+
* import { RRegex } from "rregex"
|
|
765
|
+
*
|
|
648
766
|
* const text = "I categorically deny having triskaidekaphobia."
|
|
649
|
-
* const m = new
|
|
767
|
+
* const m = new RRegex("\\b\\w{13}\\b").find(text)
|
|
650
768
|
* expect(m.start).toBe(2)
|
|
651
769
|
* expect(m.end).toBe(15)
|
|
652
770
|
* ```
|
|
@@ -680,8 +798,10 @@ export class RRegex {
|
|
|
680
798
|
* word characters:
|
|
681
799
|
*
|
|
682
800
|
* ```typescript
|
|
801
|
+
* import { RRegex } from "rregex"
|
|
802
|
+
*
|
|
683
803
|
* const text = "Retroactively relinquishing remunerations is reprehensible."
|
|
684
|
-
* const matches = new
|
|
804
|
+
* const matches = new RRegex("\\b\\w{13}\\b").findAll(text)
|
|
685
805
|
* ```
|
|
686
806
|
*
|
|
687
807
|
* @see https://docs.rs/regex/latest/regex/struct.Regex.html#method.find_iter
|
|
@@ -712,7 +832,9 @@ export class RRegex {
|
|
|
712
832
|
* year separately.
|
|
713
833
|
*
|
|
714
834
|
* ```typescript
|
|
715
|
-
*
|
|
835
|
+
* import { RRegex } from "rregex"
|
|
836
|
+
*
|
|
837
|
+
* const re = new RRegex("'([^']+)'\\s+\\((\\d{4})\)")
|
|
716
838
|
* const text = "Not my favorite movie: 'Citizen Kane' (1941)."
|
|
717
839
|
* const caps = re.captures(text)
|
|
718
840
|
* expect(caps.get[1].value).toBe("Citizen Kane")
|
|
@@ -726,7 +848,9 @@ export class RRegex {
|
|
|
726
848
|
* We can make this example a bit clearer by using *named* capture groups:
|
|
727
849
|
*
|
|
728
850
|
* ```typescript
|
|
729
|
-
*
|
|
851
|
+
* import { RRegex } from "rregex"
|
|
852
|
+
*
|
|
853
|
+
* const re = new RRegex("'(?P<title>[^']+)'\\s+\\((?P<year>\\d{4})\)")
|
|
730
854
|
* const text = "Not my favorite movie: 'Citizen Kane' (1941)."
|
|
731
855
|
* const caps = re.captures(text)
|
|
732
856
|
* expect(caps.name["title"].value).toBe("Citizen Kane")
|
|
@@ -750,7 +874,9 @@ export class RRegex {
|
|
|
750
874
|
* some text, where the movie is formatted like "'Title' (xxxx)":
|
|
751
875
|
*
|
|
752
876
|
* ```typescript
|
|
753
|
-
*
|
|
877
|
+
* import { RRegex } from "rregex"
|
|
878
|
+
*
|
|
879
|
+
* const re = new RRegex("'(?P<title>[^']+)'\\s+\\((?P<year>\\d{4})\\)")
|
|
754
880
|
* const text = "'Citizen Kane' (1941), 'The Wizard of Oz' (1939), 'M' (1931)."
|
|
755
881
|
* for caps of re.captures_iter(text) {
|
|
756
882
|
* console.log(
|
|
@@ -781,6 +907,8 @@ export class RRegex {
|
|
|
781
907
|
* ## Example
|
|
782
908
|
*
|
|
783
909
|
* ```typescript
|
|
910
|
+
* import { RRegex } from "rregex"
|
|
911
|
+
*
|
|
784
912
|
* const re1 = new RRegex("(?P<y>\\d{4})-(?P<m>\\d{2})-(?P<d>\\d{2})")
|
|
785
913
|
* expect(re1.capturesLength()).toEqual(4)
|
|
786
914
|
*
|
|
@@ -834,7 +962,9 @@ export class RRegex {
|
|
|
834
962
|
* In typical usage, this can just be a normal string:
|
|
835
963
|
*
|
|
836
964
|
* ```typescript
|
|
837
|
-
*
|
|
965
|
+
* import { RRegex } from "rregex"
|
|
966
|
+
*
|
|
967
|
+
* const re = new RRegex("[^01]+")
|
|
838
968
|
* expect(re.replace("1078910", "").toBe("1010")
|
|
839
969
|
* ```
|
|
840
970
|
*
|
|
@@ -842,7 +972,9 @@ export class RRegex {
|
|
|
842
972
|
* groups:
|
|
843
973
|
*
|
|
844
974
|
* ```typescript
|
|
845
|
-
*
|
|
975
|
+
* import { RRegex } from "rregex"
|
|
976
|
+
*
|
|
977
|
+
* const re = new RRegex("(?P<last>[^,\\s]+),\\s+(?P<first>\\S+)")
|
|
846
978
|
* const result = re.replace("Springsteen, Bruce", "$first $last")
|
|
847
979
|
* expect(result).toBe("Bruce Springsteen")
|
|
848
980
|
* ```
|
|
@@ -856,7 +988,9 @@ export class RRegex {
|
|
|
856
988
|
* underscore:
|
|
857
989
|
*
|
|
858
990
|
* ```typescript
|
|
859
|
-
*
|
|
991
|
+
* import { RRegex } from "rregex"
|
|
992
|
+
*
|
|
993
|
+
* const re = new RRegex("(?P<first>\\w+)\\s+(?P<second>\\w+)")
|
|
860
994
|
* const result = re.replace("deep fried", "${first}_$second")
|
|
861
995
|
* expect(result).toBe("deep_fried")
|
|
862
996
|
* ```
|
|
@@ -913,7 +1047,9 @@ export class RRegex {
|
|
|
913
1047
|
* To split a string delimited by arbitrary amounts of spaces or tabs:
|
|
914
1048
|
*
|
|
915
1049
|
* ```typescript
|
|
916
|
-
*
|
|
1050
|
+
* import { RRegex } from "rregex"
|
|
1051
|
+
*
|
|
1052
|
+
* const re = new RRegex(r"[ \\t]+")
|
|
917
1053
|
* const fields = re.split("a b \t c\td e")
|
|
918
1054
|
* expect(fields).toEqual(["a", "b", "c", "d", "e"])
|
|
919
1055
|
* ```
|
|
@@ -937,7 +1073,9 @@ export class RRegex {
|
|
|
937
1073
|
* Get the first two words in some text:
|
|
938
1074
|
*
|
|
939
1075
|
* ```typescript
|
|
940
|
-
*
|
|
1076
|
+
* import { RRegex } from "rregex"
|
|
1077
|
+
*
|
|
1078
|
+
* const re = new RRegex(r"\\W+")
|
|
941
1079
|
* const fields = re.splitn("Hey! How are you?", 3)
|
|
942
1080
|
* expect(fields).toEqual(["Hey", "How", "are you?"])
|
|
943
1081
|
* ```
|
|
@@ -969,8 +1107,10 @@ export class RRegex {
|
|
|
969
1107
|
* `a`.
|
|
970
1108
|
*
|
|
971
1109
|
* ```typescript
|
|
1110
|
+
* import { RRegex } from "rregex"
|
|
1111
|
+
*
|
|
972
1112
|
* const text = "aaaaa"
|
|
973
|
-
* const = new
|
|
1113
|
+
* const = new RRegex("a+").shortest_match(text)
|
|
974
1114
|
* expect(pos).toBe(1)
|
|
975
1115
|
* ```
|
|
976
1116
|
*
|
|
@@ -1029,6 +1169,8 @@ export class RRegexSet {
|
|
|
1029
1169
|
* Create a new regex set from an iterator of strings:
|
|
1030
1170
|
*
|
|
1031
1171
|
* ```typescript
|
|
1172
|
+
* import { RegexSet } from "rregex"
|
|
1173
|
+
*
|
|
1032
1174
|
* const set = new RegexSet(["\\w+", "\\d+"])
|
|
1033
1175
|
* expect(set.is_match("foo")).toBe(true)
|
|
1034
1176
|
* ```
|
|
@@ -1055,6 +1197,8 @@ export class RRegexSet {
|
|
|
1055
1197
|
* Tests whether a set matches some text:
|
|
1056
1198
|
*
|
|
1057
1199
|
* ```typescript
|
|
1200
|
+
* import { RegexSet } from "rregex"
|
|
1201
|
+
*
|
|
1058
1202
|
* const set = new RegexSet(["\\w+", "\\d+"])
|
|
1059
1203
|
* expect(set.is_match("foo")).toBe(true)
|
|
1060
1204
|
* expect(!set.is_match("☃")).toBe(false)
|
|
@@ -1084,6 +1228,8 @@ export class RRegexSet {
|
|
|
1084
1228
|
* Tests which regular expressions match the given text:
|
|
1085
1229
|
*
|
|
1086
1230
|
* ```typescript
|
|
1231
|
+
* import { RegexSet } from "rregex"
|
|
1232
|
+
*
|
|
1087
1233
|
* const set = new RegexSet([
|
|
1088
1234
|
* "\\w+",
|
|
1089
1235
|
* "\\d+",
|