@the-w1zard/dx 1.0.8 → 1.0.9
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.html +823 -121
- package/README.md +288 -107
- package/dist/index.js +12967 -0
- package/package.json +21 -6
- package/src/dx.d.ts +2 -10
- package/src/array.js +0 -221
- package/src/array.ts +0 -301
- package/src/boolean_byte.js +0 -22
- package/src/boolean_byte.ts +0 -23
- package/src/class.js +0 -1
- package/src/class.ts +0 -0
- package/src/date.js +0 -649
- package/src/date.ts +0 -660
- package/src/error.js +0 -60
- package/src/error.ts +0 -81
- package/src/index.js +0 -18
- package/src/index.ts +0 -18
- package/src/iterator.js +0 -386
- package/src/iterator.ts +0 -407
- package/src/map.js +0 -495
- package/src/map.ts +0 -513
- package/src/map_byte.js +0 -483
- package/src/map_byte.ts +0 -422
- package/src/number.js +0 -178
- package/src/number.ts +0 -212
- package/src/object.js +0 -217
- package/src/object.ts +0 -291
- package/src/promise.js +0 -206
- package/src/promise.ts +0 -309
- package/src/reg_ex.js +0 -479
- package/src/reg_ex.ts +0 -423
- package/src/set.js +0 -227
- package/src/set.ts +0 -198
- package/src/set_byte.js +0 -261
- package/src/set_byte.ts +0 -223
- package/src/soa_codec.js +0 -28
- package/src/soa_codec.ts +0 -27
- package/src/string.js +0 -133
- package/src/string.ts +0 -175
- package/src/unit8array.js +0 -272
- package/src/unit8array.ts +0 -374
- package/src/url_search_params.js +0 -172
- package/src/url_search_params.ts +0 -187
- package/src/weak_map.js +0 -136
- package/src/weak_map.ts +0 -155
- package/src/weak_set.js +0 -92
- package/src/weak_set.ts +0 -101
package/src/reg_ex.js
DELETED
|
@@ -1,479 +0,0 @@
|
|
|
1
|
-
function escapeRegex(str) {
|
|
2
|
-
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3
|
-
}
|
|
4
|
-
function escapeForClass(chars) {
|
|
5
|
-
return chars.replace(/[\]\\^-]/g, "\\$&");
|
|
6
|
-
}
|
|
7
|
-
function toSource(input) {
|
|
8
|
-
if (typeof input === "string")
|
|
9
|
-
return input;
|
|
10
|
-
return input.source;
|
|
11
|
-
}
|
|
12
|
-
function isBalancedGroup(src) {
|
|
13
|
-
if (src[0] !== "(" || src[src.length - 1] !== ")")
|
|
14
|
-
return false;
|
|
15
|
-
let depth = 0;
|
|
16
|
-
for (let i = 0; i < src.length; i++) {
|
|
17
|
-
if (src[i] === "\\") {
|
|
18
|
-
i++;
|
|
19
|
-
continue;
|
|
20
|
-
}
|
|
21
|
-
if (src[i] === "(")
|
|
22
|
-
depth++;
|
|
23
|
-
else if (src[i] === ")") {
|
|
24
|
-
depth--;
|
|
25
|
-
if (depth === 0 && i !== src.length - 1)
|
|
26
|
-
return false;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
return true;
|
|
30
|
-
}
|
|
31
|
-
function isBalancedClass(src) {
|
|
32
|
-
if (src[0] !== "[" || src[src.length - 1] !== "]")
|
|
33
|
-
return false;
|
|
34
|
-
for (let i = 1; i < src.length - 1; i++) {
|
|
35
|
-
if (src[i] === "\\") {
|
|
36
|
-
i++;
|
|
37
|
-
continue;
|
|
38
|
-
}
|
|
39
|
-
if (src[i] === "]")
|
|
40
|
-
return false;
|
|
41
|
-
}
|
|
42
|
-
return true;
|
|
43
|
-
}
|
|
44
|
-
function wrapForConcat(src) {
|
|
45
|
-
if (src.length === 1)
|
|
46
|
-
return src;
|
|
47
|
-
if (/^\\.$/.test(src))
|
|
48
|
-
return src;
|
|
49
|
-
if (/^\[.*\]$/.test(src) && isBalancedClass(src))
|
|
50
|
-
return src;
|
|
51
|
-
if (/^\(.*\)$/.test(src) && isBalancedGroup(src))
|
|
52
|
-
return src;
|
|
53
|
-
return `(?:${src})`;
|
|
54
|
-
}
|
|
55
|
-
function mergeFlags(base, parts) {
|
|
56
|
-
const set = new Set(base.split(""));
|
|
57
|
-
for (const p of parts) {
|
|
58
|
-
if (p instanceof REC2 || p instanceof RegExp)
|
|
59
|
-
p.flags.split("").forEach(f => set.add(f));
|
|
60
|
-
}
|
|
61
|
-
return [...set].join("");
|
|
62
|
-
}
|
|
63
|
-
export class REC2 {
|
|
64
|
-
#re;
|
|
65
|
-
constructor(source, flags = "") {
|
|
66
|
-
this.#re = new RegExp(source, flags);
|
|
67
|
-
}
|
|
68
|
-
// [PASSTHROUGH]
|
|
69
|
-
// #region ══════════════════════════════════════════════════════════════════════════════════════════════════════
|
|
70
|
-
get source() { return this.#re.source; }
|
|
71
|
-
get flags() { return this.#re.flags; }
|
|
72
|
-
get global() { return this.#re.global; }
|
|
73
|
-
get ignoreCase() { return this.#re.ignoreCase; }
|
|
74
|
-
get multiline() { return this.#re.multiline; }
|
|
75
|
-
get sticky() { return this.#re.sticky; }
|
|
76
|
-
get unicode() { return this.#re.unicode; }
|
|
77
|
-
get dotAll() { return this.#re.dotAll; }
|
|
78
|
-
get hasIndices() { return this.#re.hasIndices; }
|
|
79
|
-
toRegExp() { return new RegExp(this.#re.source, this.#re.flags); }
|
|
80
|
-
// #endregion
|
|
81
|
-
// [BASE]
|
|
82
|
-
// #region ══════════════════════════════════════════════════════════════════════════════════════════════════════
|
|
83
|
-
test(str) { return new RegExp(this.#re.source, this.#re.flags).test(str); }
|
|
84
|
-
h(str) { return this.test(str); }
|
|
85
|
-
exec(str) {
|
|
86
|
-
return new RegExp(this.#re.source, this.#re.flags).exec(str);
|
|
87
|
-
}
|
|
88
|
-
g(str) { return this.exec(str)?.[0]; }
|
|
89
|
-
at(str, n) {
|
|
90
|
-
const all = this.matches(str);
|
|
91
|
-
const i = n < 0 ? all.length + n : n;
|
|
92
|
-
return all[i]?.match;
|
|
93
|
-
}
|
|
94
|
-
groups(str) { return this.exec(str)?.groups; }
|
|
95
|
-
group(str, name) {
|
|
96
|
-
const m = this.exec(str);
|
|
97
|
-
if (!m)
|
|
98
|
-
return undefined;
|
|
99
|
-
return typeof name === "number" ? m[name] : m.groups?.[name];
|
|
100
|
-
}
|
|
101
|
-
index(str) { return this.exec(str)?.index ?? -1; }
|
|
102
|
-
lastIndexOf(str) {
|
|
103
|
-
const all = this.matches(str);
|
|
104
|
-
return all.length ? all[all.length - 1].index : -1;
|
|
105
|
-
}
|
|
106
|
-
span(str) {
|
|
107
|
-
const m = this.exec(str);
|
|
108
|
-
return m ? [m.index, m.index + m[0].length] : undefined;
|
|
109
|
-
}
|
|
110
|
-
// #endregion
|
|
111
|
-
// [COLLECT]
|
|
112
|
-
// #region ══════════════════════════════════════════════════════════════════════════════════════════════════════
|
|
113
|
-
all(str) { return this.matches(str).map(m => m.match); }
|
|
114
|
-
matches(str) {
|
|
115
|
-
const out = [];
|
|
116
|
-
for (const m of str.matchAll(this.#forceGlobal())) {
|
|
117
|
-
out.push({
|
|
118
|
-
match: m[0],
|
|
119
|
-
index: m.index,
|
|
120
|
-
lastIndex: m.index + m[0].length,
|
|
121
|
-
groups: m.groups,
|
|
122
|
-
raw: m,
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
|
-
return out;
|
|
126
|
-
}
|
|
127
|
-
allGroups(str) { return this.matches(str).map(m => m.groups ?? {}); }
|
|
128
|
-
allSpans(str) { return this.matches(str).map(m => [m.index, m.lastIndex]); }
|
|
129
|
-
count(str) { return this.matches(str).length; }
|
|
130
|
-
positions(str) { return this.matches(str).map(m => m.index); }
|
|
131
|
-
// #endregion
|
|
132
|
-
// [REPLACE]
|
|
133
|
-
// #region ══════════════════════════════════════════════════════════════════════════════════════════════════════
|
|
134
|
-
#parseArgs(args, str) {
|
|
135
|
-
const groups = typeof args[args.length - 1] === "object" ? args.pop() : undefined;
|
|
136
|
-
const index = args[args.length - 2];
|
|
137
|
-
const raw = [args[0], ...args.slice(1, -2)];
|
|
138
|
-
raw.index = index;
|
|
139
|
-
raw.input = str;
|
|
140
|
-
raw.groups = groups;
|
|
141
|
-
return [raw, groups, index];
|
|
142
|
-
}
|
|
143
|
-
replace(str, fn) {
|
|
144
|
-
const re = new RegExp(this.#re.source, this.#re.flags);
|
|
145
|
-
if (typeof fn === "string")
|
|
146
|
-
return str.replace(re, fn);
|
|
147
|
-
return str.replace(re, (...a) => {
|
|
148
|
-
const [raw, groups, index] = this.#parseArgs(a, str);
|
|
149
|
-
return fn(raw, groups, index, str);
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
replaceFirst(str, fn) {
|
|
153
|
-
const re = new RegExp(this.#re.source, this.#re.flags.replace("g", ""));
|
|
154
|
-
if (typeof fn === "string")
|
|
155
|
-
return str.replace(re, fn);
|
|
156
|
-
return str.replace(re, (...a) => {
|
|
157
|
-
const [raw, groups, index] = this.#parseArgs(a, str);
|
|
158
|
-
return fn(raw, groups, index, str);
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
|
-
replaceAt(str, n, fn) {
|
|
162
|
-
const total = this.matches(str).length;
|
|
163
|
-
const target = n < 0 ? total + n : n;
|
|
164
|
-
let i = 0;
|
|
165
|
-
return str.replace(this.#forceGlobal(), (...a) => {
|
|
166
|
-
const match = a[0];
|
|
167
|
-
const isTarget = i === target;
|
|
168
|
-
i++;
|
|
169
|
-
if (!isTarget)
|
|
170
|
-
return match;
|
|
171
|
-
if (typeof fn === "string")
|
|
172
|
-
return fn;
|
|
173
|
-
const [raw, groups, index] = this.#parseArgs(a, str);
|
|
174
|
-
return fn(raw, groups, index, str);
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
remove(str) { return this.replace(str, ""); }
|
|
178
|
-
removeFirst(str) { return this.replaceFirst(str, ""); }
|
|
179
|
-
split(str, limit) { return str.split(new RegExp(this.#re.source, this.#re.flags), limit); }
|
|
180
|
-
splitKeep(str) {
|
|
181
|
-
const flags = this.flags.includes("g") ? this.flags : this.flags + "g";
|
|
182
|
-
const re = new RegExp(`(${this.#re.source})`, flags);
|
|
183
|
-
return str.split(re).filter(s => s !== "");
|
|
184
|
-
}
|
|
185
|
-
// #endregion
|
|
186
|
-
// [BUILD]
|
|
187
|
-
// #region ══════════════════════════════════════════════════════════════════════════════════════════════════════
|
|
188
|
-
withFlags(flags) { return new REC2(this.#re.source, flags); }
|
|
189
|
-
addFlag(flag) { return this.flags.includes(flag) ? this.clone() : new REC2(this.#re.source, this.flags + flag); }
|
|
190
|
-
removeFlag(flag) { return new REC2(this.#re.source, this.flags.replace(flag, "")); }
|
|
191
|
-
toggleFlag(flag) { return this.flags.includes(flag) ? this.removeFlag(flag) : this.addFlag(flag); }
|
|
192
|
-
withSource(src) { return new REC2(src, this.flags); }
|
|
193
|
-
and(...parts) {
|
|
194
|
-
const src = [this.#re.source, ...parts.map(toSource)].map(wrapForConcat).join("");
|
|
195
|
-
return new REC2(src, mergeFlags(this.flags, parts));
|
|
196
|
-
}
|
|
197
|
-
or(...parts) {
|
|
198
|
-
const src = `(?:${[this.#re.source, ...parts.map(toSource)].join("|")})`;
|
|
199
|
-
return new REC2(src, mergeFlags(this.flags, parts));
|
|
200
|
-
}
|
|
201
|
-
cap(expr) {
|
|
202
|
-
return new REC2(`(${expr === undefined ? this.#re.source : toSource(expr)})`, this.flags);
|
|
203
|
-
}
|
|
204
|
-
namedGroup(name, expr) { return new REC2(`(?<${name}>${toSource(expr)})`, this.flags); }
|
|
205
|
-
nonCap(expr) { return new REC2(`(?:${toSource(expr)})`, this.flags); }
|
|
206
|
-
lookahead(expr) { return new REC2(`(?=${toSource(expr)})`, this.flags); }
|
|
207
|
-
notLookahead(expr) { return new REC2(`(?!${toSource(expr)})`, this.flags); }
|
|
208
|
-
lookbehind(expr) { return new REC2(`(?<=${toSource(expr)})`, this.flags); }
|
|
209
|
-
notLookbehind(expr) { return new REC2(`(?<!${toSource(expr)})`, this.flags); }
|
|
210
|
-
optional(expr) { return new REC2(`${wrapForConcat(expr === undefined ? this.#re.source : toSource(expr))}?`, this.flags); }
|
|
211
|
-
many(expr) { return new REC2(`${wrapForConcat(expr === undefined ? this.#re.source : toSource(expr))}+`, this.flags); }
|
|
212
|
-
maybeMany(expr) { return new REC2(`${wrapForConcat(expr === undefined ? this.#re.source : toSource(expr))}*`, this.flags); }
|
|
213
|
-
repeat(n, max, expr) {
|
|
214
|
-
const src = wrapForConcat(expr === undefined ? this.#re.source : toSource(expr));
|
|
215
|
-
return new REC2(`${src}${max === undefined ? `{${n}}` : `{${n},${max}}`}`, this.flags);
|
|
216
|
-
}
|
|
217
|
-
/**
|
|
218
|
-
* [BEST-EFFORT] Appends `?` after a trailing `*`/`+`/`?`/`{n,m}` to make it lazy.
|
|
219
|
-
* Only looks at the END of the source — it's not a regex parser, it won't find
|
|
220
|
-
* quantifiers buried mid-pattern. Meant to be chained right after .optional()/
|
|
221
|
-
* .many()/.maybeMany()/.repeat(), where the quantifier IS the last token.
|
|
222
|
-
* If no trailing quantifier is found, returns an unchanged clone.
|
|
223
|
-
*/
|
|
224
|
-
lazy() {
|
|
225
|
-
const src = this.#re.source;
|
|
226
|
-
if (/[*+?]$/.test(src) || /\{\d+(,\d*)?\}$/.test(src))
|
|
227
|
-
return new REC2(`${src}?`, this.flags);
|
|
228
|
-
return this.clone();
|
|
229
|
-
}
|
|
230
|
-
// #endregion
|
|
231
|
-
// [DEBUG/SAFETY]
|
|
232
|
-
// #region ══════════════════════════════════════════════════════════════════════════════════════════════════════
|
|
233
|
-
$trace(str) { return this.matches(str); }
|
|
234
|
-
$explain() {
|
|
235
|
-
const flagNotes = [];
|
|
236
|
-
if (this.global)
|
|
237
|
-
flagNotes.push("matches all occurrences (g)");
|
|
238
|
-
if (this.ignoreCase)
|
|
239
|
-
flagNotes.push("case-insensitive (i)");
|
|
240
|
-
if (this.multiline)
|
|
241
|
-
flagNotes.push("^/$ match per line (m)");
|
|
242
|
-
if (this.dotAll)
|
|
243
|
-
flagNotes.push(". matches newlines (s)");
|
|
244
|
-
if (this.sticky)
|
|
245
|
-
flagNotes.push("sticky from lastIndex (y)");
|
|
246
|
-
if (this.unicode)
|
|
247
|
-
flagNotes.push("unicode mode (u)");
|
|
248
|
-
return [...flagNotes, "pattern:", ...explainSource(this.#re.source).map(l => ` ${l}`)];
|
|
249
|
-
}
|
|
250
|
-
$risky() { return /\([^)]*[+*][^)]*\)[+*]/.test(this.#re.source); }
|
|
251
|
-
$bench(str, iterations = 1000) {
|
|
252
|
-
const re = new RegExp(this.#re.source, this.flags);
|
|
253
|
-
const start = performance.now();
|
|
254
|
-
for (let i = 0; i < iterations; i++)
|
|
255
|
-
re.test(str);
|
|
256
|
-
return performance.now() - start;
|
|
257
|
-
}
|
|
258
|
-
// #endregion
|
|
259
|
-
// [CODECS]
|
|
260
|
-
// #region ══════════════════════════════════════════════════════════════════════════════════════════════════════
|
|
261
|
-
toString() { return this.#re.toString(); }
|
|
262
|
-
equals(other) {
|
|
263
|
-
const o = typeof other === "string" ? new RegExp(other)
|
|
264
|
-
: other instanceof RegExp ? other
|
|
265
|
-
: other; // remaining case is RE2_IF, which has .source/.flags already
|
|
266
|
-
return this.#re.source === o.source && this.#re.flags === o.flags;
|
|
267
|
-
}
|
|
268
|
-
clone() { return new REC2(this.#re.source, this.flags); }
|
|
269
|
-
#forceGlobal() {
|
|
270
|
-
const flags = this.flags.includes("g") ? this.flags : this.flags + "g";
|
|
271
|
-
return new RegExp(this.#re.source, flags);
|
|
272
|
-
}
|
|
273
|
-
static fromRegExp(re) { return new REC2(re.source, re.flags); }
|
|
274
|
-
}
|
|
275
|
-
function explainSource(source) {
|
|
276
|
-
let i = 0;
|
|
277
|
-
const n = source.length;
|
|
278
|
-
const peek = () => source[i];
|
|
279
|
-
const eof = () => i >= n;
|
|
280
|
-
function parseAlt() {
|
|
281
|
-
const branches = [parseSeq()];
|
|
282
|
-
while (!eof() && peek() === "|") {
|
|
283
|
-
i++;
|
|
284
|
-
branches.push(parseSeq());
|
|
285
|
-
}
|
|
286
|
-
return branches.length === 1 ? branches[0] : { desc: "one of:", children: branches };
|
|
287
|
-
}
|
|
288
|
-
function parseSeq() {
|
|
289
|
-
const items = [];
|
|
290
|
-
while (!eof() && peek() !== "|" && peek() !== ")")
|
|
291
|
-
items.push(parseQuantified());
|
|
292
|
-
if (items.length === 0)
|
|
293
|
-
return { desc: "(empty)" };
|
|
294
|
-
return items.length === 1 ? items[0] : { desc: "in sequence:", children: items };
|
|
295
|
-
}
|
|
296
|
-
function parseQuantified() {
|
|
297
|
-
const atom = parseAtom();
|
|
298
|
-
if (eof())
|
|
299
|
-
return atom;
|
|
300
|
-
let quant;
|
|
301
|
-
const c = peek();
|
|
302
|
-
if (c === "*") {
|
|
303
|
-
i++;
|
|
304
|
-
quant = "zero or more";
|
|
305
|
-
}
|
|
306
|
-
else if (c === "+") {
|
|
307
|
-
i++;
|
|
308
|
-
quant = "one or more";
|
|
309
|
-
}
|
|
310
|
-
else if (c === "?") {
|
|
311
|
-
i++;
|
|
312
|
-
quant = "optional (zero or one)";
|
|
313
|
-
}
|
|
314
|
-
else if (c === "{") {
|
|
315
|
-
const m = /^\{(\d+)(,(\d*))?\}/.exec(source.slice(i));
|
|
316
|
-
if (m) {
|
|
317
|
-
i += m[0].length;
|
|
318
|
-
quant = m[2] === undefined ? `exactly ${m[1]} time${m[1] === "1" ? "" : "s"}`
|
|
319
|
-
: m[3] === "" ? `${m[1]} or more times`
|
|
320
|
-
: `between ${m[1]} and ${m[3]} times`;
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
if (!quant)
|
|
324
|
-
return atom;
|
|
325
|
-
let lazy = false;
|
|
326
|
-
if (!eof() && peek() === "?") {
|
|
327
|
-
i++;
|
|
328
|
-
lazy = true;
|
|
329
|
-
}
|
|
330
|
-
return { desc: `${atom.desc} — ${quant}${lazy ? " (lazy, as few as possible)" : ""}`, children: atom.children };
|
|
331
|
-
}
|
|
332
|
-
function parseAtom() {
|
|
333
|
-
const c = peek();
|
|
334
|
-
if (c === "(")
|
|
335
|
-
return parseGroup();
|
|
336
|
-
if (c === "[")
|
|
337
|
-
return parseClass();
|
|
338
|
-
if (c === "^") {
|
|
339
|
-
i++;
|
|
340
|
-
return { desc: "start of string/line" };
|
|
341
|
-
}
|
|
342
|
-
if (c === "$") {
|
|
343
|
-
i++;
|
|
344
|
-
return { desc: "end of string/line" };
|
|
345
|
-
}
|
|
346
|
-
if (c === ".") {
|
|
347
|
-
i++;
|
|
348
|
-
return { desc: "any character" };
|
|
349
|
-
}
|
|
350
|
-
if (c === "\\")
|
|
351
|
-
return parseEscape();
|
|
352
|
-
let lit = "";
|
|
353
|
-
while (!eof() && !"\\^$.|?*+()[]{}".includes(peek()))
|
|
354
|
-
lit += source[i++];
|
|
355
|
-
if (lit)
|
|
356
|
-
return { desc: `literally "${lit}"` };
|
|
357
|
-
i++;
|
|
358
|
-
return { desc: `literally "${c}"` };
|
|
359
|
-
}
|
|
360
|
-
function parseEscape() {
|
|
361
|
-
i++;
|
|
362
|
-
const c = source[i++];
|
|
363
|
-
const map = {
|
|
364
|
-
d: "a digit", D: "a non-digit", w: "a word character", W: "a non-word character",
|
|
365
|
-
s: "whitespace", S: "non-whitespace", b: "a word boundary", B: "not a word boundary",
|
|
366
|
-
n: "a newline", t: "a tab", r: "a carriage return",
|
|
367
|
-
};
|
|
368
|
-
return { desc: map[c] ?? `literally "${c}"` };
|
|
369
|
-
}
|
|
370
|
-
function parseClass() {
|
|
371
|
-
i++;
|
|
372
|
-
let negate = false;
|
|
373
|
-
if (peek() === "^") {
|
|
374
|
-
negate = true;
|
|
375
|
-
i++;
|
|
376
|
-
}
|
|
377
|
-
let body = "";
|
|
378
|
-
while (!eof() && peek() !== "]") {
|
|
379
|
-
body += peek() === "\\" ? source[i++] + source[i++] : source[i++];
|
|
380
|
-
}
|
|
381
|
-
i++;
|
|
382
|
-
return { desc: `${negate ? "any character NOT" : "any character"} in [${body}]` };
|
|
383
|
-
}
|
|
384
|
-
function parseGroup() {
|
|
385
|
-
i++; // consume '('
|
|
386
|
-
let kind = "capturing group";
|
|
387
|
-
if (peek() === "?") {
|
|
388
|
-
const c2 = source[i + 1];
|
|
389
|
-
if (c2 === ":") {
|
|
390
|
-
i += 2;
|
|
391
|
-
kind = "non-capturing group";
|
|
392
|
-
}
|
|
393
|
-
else if (c2 === "=") {
|
|
394
|
-
i += 2;
|
|
395
|
-
kind = "lookahead — must be followed by";
|
|
396
|
-
}
|
|
397
|
-
else if (c2 === "!") {
|
|
398
|
-
i += 2;
|
|
399
|
-
kind = "negative lookahead — must NOT be followed by";
|
|
400
|
-
}
|
|
401
|
-
else if (c2 === "<" && source[i + 2] === "=") {
|
|
402
|
-
i += 3;
|
|
403
|
-
kind = "lookbehind — must be preceded by";
|
|
404
|
-
}
|
|
405
|
-
else if (c2 === "<" && source[i + 2] === "!") {
|
|
406
|
-
i += 3;
|
|
407
|
-
kind = "negative lookbehind — must NOT be preceded by";
|
|
408
|
-
}
|
|
409
|
-
else if (c2 === "<") {
|
|
410
|
-
i += 2;
|
|
411
|
-
let nm = "";
|
|
412
|
-
while (source[i] !== ">")
|
|
413
|
-
nm += source[i++];
|
|
414
|
-
i++;
|
|
415
|
-
kind = `named capturing group "${nm}"`;
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
const inner = parseAlt();
|
|
419
|
-
if (peek() === ")")
|
|
420
|
-
i++;
|
|
421
|
-
return { desc: kind, children: [inner] };
|
|
422
|
-
}
|
|
423
|
-
const tree = parseAlt();
|
|
424
|
-
const lines = [];
|
|
425
|
-
(function render(node, depth) {
|
|
426
|
-
lines.push(`${" ".repeat(depth)}${node.desc}`);
|
|
427
|
-
node.children?.forEach(c => render(c, depth + 1));
|
|
428
|
-
})(tree, 0);
|
|
429
|
-
return lines;
|
|
430
|
-
}
|
|
431
|
-
// [STATICS / PRESETS]
|
|
432
|
-
// ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════
|
|
433
|
-
export const RECC2 = Object.assign((input, flags) => RECC2.of(input, flags), {
|
|
434
|
-
of(input, flags) {
|
|
435
|
-
if (typeof input === "string")
|
|
436
|
-
return new REC2(input, flags ?? "");
|
|
437
|
-
if (input instanceof RegExp)
|
|
438
|
-
return new REC2(input.source, flags ?? input.flags);
|
|
439
|
-
// remaining case is RE2_IF
|
|
440
|
-
return flags !== undefined ? input.withFlags(flags) : input.clone();
|
|
441
|
-
},
|
|
442
|
-
escape: escapeRegex,
|
|
443
|
-
literal(str) { return new REC2(escapeRegex(str)); },
|
|
444
|
-
seq(...parts) {
|
|
445
|
-
return new REC2(parts.map(p => wrapForConcat(toSource(p))).join(""), mergeFlags("", parts));
|
|
446
|
-
},
|
|
447
|
-
alt(...parts) {
|
|
448
|
-
return new REC2(`(?:${parts.map(toSource).join("|")})`, mergeFlags("", parts));
|
|
449
|
-
},
|
|
450
|
-
digit: new REC2("\\d"), word: new REC2("\\w"), space: new REC2("\\s"), any: new REC2("."),
|
|
451
|
-
notDigit: new REC2("\\D"), notWord: new REC2("\\W"), notSpace: new REC2("\\S"),
|
|
452
|
-
range(from, to) { return new REC2(`[${from}-${to}]`); },
|
|
453
|
-
oneOf(chars) { return new REC2(`[${escapeForClass(chars)}]`); },
|
|
454
|
-
noneOf(chars) { return new REC2(`[^${escapeForClass(chars)}]`); },
|
|
455
|
-
email: new REC2("[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}"),
|
|
456
|
-
url: new REC2("https?:\\/\\/[^\\s/$.?#].[^\\s]*", "i"),
|
|
457
|
-
uuid: new REC2("[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}", "i"),
|
|
458
|
-
ipv4: new REC2("(?:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)\\.){3}(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)"),
|
|
459
|
-
ipv6: new REC2("(?:[a-fA-F0-9]{1,4}:){7}[a-fA-F0-9]{1,4}"),
|
|
460
|
-
hex: new REC2("[0-9a-fA-F]+"),
|
|
461
|
-
hexColor: new REC2("#(?:[0-9a-fA-F]{3}){1,2}\\b"),
|
|
462
|
-
slug: new REC2("[a-z0-9]+(?:-[a-z0-9]+)*"),
|
|
463
|
-
alphaNumeric: new REC2("[a-zA-Z0-9]+"),
|
|
464
|
-
integer: new REC2("-?\\d+"),
|
|
465
|
-
float: new REC2("-?\\d+(?:\\.\\d+)?"),
|
|
466
|
-
phoneLoose: new REC2("\\+?\\d[\\d\\s().-]{6,}\\d"),
|
|
467
|
-
isoDate: new REC2("\\d{4}-\\d{2}-\\d{2}"),
|
|
468
|
-
base64: new REC2("(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?"),
|
|
469
|
-
jwt: new REC2("[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+"),
|
|
470
|
-
semver: new REC2("\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z.-]+)?(?:\\+[0-9A-Za-z.-]+)?"),
|
|
471
|
-
creditCard: new REC2("\\d{4}[\\s-]?\\d{4}[\\s-]?\\d{4}[\\s-]?\\d{1,4}"),
|
|
472
|
-
htmlTag: new REC2("<\\/?[a-zA-Z][^>]*>"),
|
|
473
|
-
whitespaceOnly: new REC2("^\\s*$"),
|
|
474
|
-
});
|
|
475
|
-
globalThis.REC2 = REC2;
|
|
476
|
-
globalThis.RE2 = RECC2;
|
|
477
|
-
/* ====================================================================================================
|
|
478
|
-
|
|
479
|
-
============================================================= ⇦✦──⇦✦──✦──✦──← 'pew pew' 🧙 [REGEX] */
|