@amritk/nish-x86_64-linux 0.18.0 → 0.21.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 (104) hide show
  1. package/INSTALL.md +58 -28
  2. package/bin/nish +0 -0
  3. package/docs/AI.md +2759 -0
  4. package/package.json +3 -2
  5. package/runtime/browser.d.ts +14 -0
  6. package/runtime/browser.mjs +88 -0
  7. package/runtime/core/arrays.d.ts +18 -0
  8. package/runtime/core/arrays.mjs +125 -0
  9. package/runtime/core/checks.mjs +20 -0
  10. package/runtime/core/host.d.ts +70 -0
  11. package/runtime/core/host.mjs +311 -0
  12. package/runtime/core/numbers.d.ts +53 -0
  13. package/runtime/core/numbers.mjs +316 -0
  14. package/runtime/core/result.d.ts +41 -0
  15. package/runtime/core/result.mjs +127 -0
  16. package/runtime/core/strings.d.ts +14 -0
  17. package/runtime/core/strings.mjs +169 -0
  18. package/runtime/core.d.ts +9 -0
  19. package/runtime/core.mjs +55 -0
  20. package/runtime/host.d.ts +6 -0
  21. package/runtime/nish.d.ts +99 -5
  22. package/runtime/nish.h +289 -19
  23. package/runtime/nish.mjs +39 -3
  24. package/runtime/node.mjs +383 -0
  25. package/runtime/runtime-aes.c +490 -0
  26. package/runtime/runtime-bytes.c +401 -0
  27. package/runtime/runtime-cross.c +366 -0
  28. package/runtime/runtime-host.c +8 -7
  29. package/runtime/runtime-net.c +93 -34
  30. package/runtime/runtime-os.c +214 -16
  31. package/runtime/runtime-parallel.c +590 -107
  32. package/runtime/runtime-simd.c +235 -11
  33. package/runtime/runtime-term.c +504 -0
  34. package/runtime/runtime-wasm.c +5 -14
  35. package/runtime/runtime.c +257 -17
  36. package/runtime/shim.mjs +6 -915
  37. package/runtime/sqlite/LICENSE.md +85 -0
  38. package/runtime/sqlite/README.md +45 -0
  39. package/runtime/sqlite/nish-sqlite.c +359 -0
  40. package/runtime/sqlite/sqlite3.c +271131 -0
  41. package/runtime/sqlite/sqlite3.h +14447 -0
  42. package/scripts/build.sh +437 -28
  43. package/std/LICENSE-fast-float +23 -0
  44. package/std/LICENSE-fdlibm +8 -0
  45. package/std/README.md +108 -58
  46. package/std/base64.ts +116 -0
  47. package/std/bytes.ts +419 -0
  48. package/std/cli.ts +328 -0
  49. package/std/collections.ts +6 -5
  50. package/std/crypto/LICENSE-crypt_blowfish +26 -0
  51. package/std/crypto/LICENSE-libsecp256k1 +19 -0
  52. package/std/crypto/aes.ts +238 -76
  53. package/std/crypto/base64url.ts +125 -0
  54. package/std/crypto/bcrypt.ts +760 -0
  55. package/std/crypto/chacha20poly1305.ts +43 -9
  56. package/std/crypto/ed25519.ts +840 -0
  57. package/std/crypto/hkdf.ts +304 -12
  58. package/std/crypto/hmac.ts +382 -54
  59. package/std/crypto/p256.ts +932 -61
  60. package/std/crypto/x25519.ts +55 -37
  61. package/std/crypto/x509-random.ts +71 -0
  62. package/std/crypto/x509.ts +20 -131
  63. package/std/json-tokenizer.ts +4237 -0
  64. package/std/json-typed.ts +1014 -0
  65. package/std/json-value.ts +1182 -0
  66. package/std/json.ts +1202 -34
  67. package/std/math.ts +608 -0
  68. package/std/net/buffer-pool.ts +151 -0
  69. package/std/net/hpack.ts +416 -112
  70. package/std/net/http-fields.ts +93 -0
  71. package/std/net/http1-server.ts +501 -62
  72. package/std/net/http1.ts +31 -6
  73. package/std/net/http2-tls.ts +112 -24
  74. package/std/net/http2.ts +333 -75
  75. package/std/net/http3-server.ts +261 -43
  76. package/std/net/http3.ts +187 -28
  77. package/std/net/postgres.ts +1875 -0
  78. package/std/net/prefork.ts +412 -0
  79. package/std/net/quic-listener.ts +108 -11
  80. package/std/net/quic-packet.ts +243 -82
  81. package/std/net/quic-recovery.ts +361 -30
  82. package/std/net/quic-stream.ts +424 -24
  83. package/std/net/quic.ts +519 -167
  84. package/std/net/router.ts +676 -0
  85. package/std/net/tls/codec.ts +426 -27
  86. package/std/net/tls/record-server.ts +478 -132
  87. package/std/net/tls/record.ts +133 -42
  88. package/std/net/tls/schedule.ts +464 -55
  89. package/std/net/tls/ticket.ts +610 -0
  90. package/std/net/tls-tcp.ts +196 -53
  91. package/std/net/tls.ts +1018 -260
  92. package/std/net/url.ts +571 -0
  93. package/std/random.ts +94 -0
  94. package/std/regex.ts +4274 -0
  95. package/std/sqlite.ts +391 -0
  96. package/std/term-io.ts +978 -0
  97. package/std/term-pixels.ts +874 -0
  98. package/std/term-probe.ts +459 -0
  99. package/std/term-rate.ts +993 -0
  100. package/std/term.ts +2453 -0
  101. package/std/text.ts +90 -7
  102. package/std/threads.ts +438 -14
  103. package/std/yaml-lines.ts +141 -0
  104. package/std/yaml.ts +4675 -0
package/std/regex.ts ADDED
@@ -0,0 +1,4274 @@
1
+ /**
2
+ * `nish/regex` — regular expressions whose search time is linear in the
3
+ * input, whatever the pattern.
4
+ *
5
+ * import { Regex, RegexMatch, regexCompile } from "nish/regex";
6
+ *
7
+ * const compiled = regexCompile("(?<year>\\d{4})-(?<month>\\d{2})", "")
8
+ * if (compiled.isErr()) {
9
+ * // compiled.error is the reason, such as "regex: missing ) at byte 0"
10
+ * } else {
11
+ * const re: Regex = compiled.value
12
+ * const m: RegexMatch = new RegexMatch()
13
+ * if (re.exec("due 2026-10", 0, m)) {
14
+ * // m.start() 4, m.end() 11, m.named("month") "10"
15
+ * }
16
+ * re.replaceAll("2026-10 and 2027-01", "$<month>/$<year>") // "10/2026 and 01/2027"
17
+ * }
18
+ *
19
+ * **The syntax is JavaScript's**, with the `u` flag's strictness: literals,
20
+ * `.`, classes `[a-z]` and `[^…]`, `\d \w \s` and their negations, `\b \B ^
21
+ * $`, groups `(…)`, `(?:…)` and `(?<name>…)`, `|`, and the quantifiers `* + ?
22
+ * {n} {n,} {n,m}` with their lazy `?` forms. Escapes are `\n \r \t \f \v \0`,
23
+ * `\cX`, `\xHH`, `\uHHHH`, `\u{H…}` and a backslash before any syntax
24
+ * character; a `{`, `}` or `]` that does not belong to a quantifier or a
25
+ * class is an error rather than a literal, so write `\{`. The flags are `i`
26
+ * (ASCII letters only), `m` (`^` and `$` at line terminators) and `s` (`.`
27
+ * matches them too). **Backreferences and
28
+ * lookaround are refused at compile time**: they are what makes a matcher
29
+ * backtrack, and this one never does. A repeat count is at most 1,000, a
30
+ * pattern at most 100,000 instructions once its repeats are written out,
31
+ * 1,024 capturing groups and 10,000 ranges in one class.
32
+ *
33
+ * **Matching never backtracks.** A search runs a lazy DFA first: it builds
34
+ * its states from the compiled program as the text needs them, so it reads a
35
+ * byte per step and the leftmost-first match's end comes from a table lookup.
36
+ * A reverse DFA, run back from that end, finds where the match starts, and a
37
+ * Pike VM, the construction RE2, Go's `regexp` and Rust's `regex` use, runs
38
+ * from that start only when a group's capture is wanted. A pattern with a
39
+ * literal no match can avoid, such as `\w+@example`, is searched for by that
40
+ * literal and the match grown both ways from it. Every engine follows every
41
+ * alternative at once, so a match costs O(pattern × input) at worst and
42
+ * `(a+)+$` against a thousand `a`s and a `b` is as quick as `a+$`. Finding
43
+ * every match (`findAll`, `count`, `replaceAll`, `split`) is one search per
44
+ * match, so it is quadratic when a preferred branch reads far ahead before a
45
+ * short one wins: `<[^>]*>|<` over a text of `<`s rereads the rest of the text
46
+ * for each, as it does in RE2 and Rust's `regex`. Which match
47
+ * is found is the one JavaScript finds — the leftmost, and of those the one
48
+ * the first alternative and the greediest quantifier prefer — and a group
49
+ * inside a quantifier keeps only its last iteration's capture, as in
50
+ * JavaScript. An iteration past a quantifier's minimum that matches empty is
51
+ * rejected, as JavaScript rejects it. The VM keeps one thread per
52
+ * instruction, position and set of iterations begun, so `(a*?)+?$` on "aa"
53
+ * captures "a", as JavaScript does; past three such quantifiers nested in one
54
+ * another the deeper ones share their threads, and a capture there can be a
55
+ * different iteration's than JavaScript's.
56
+ *
57
+ * **Strings are UTF-8 and offsets are bytes**, like `s.length`. The VM steps
58
+ * a code point at a time, so `.` and `[^a]` consume a whole character; a byte
59
+ * that is not part of valid UTF-8 is one character of its own, which no
60
+ * literal and no range matches. `\w`, `\d` and `\b` are ASCII, as they are in
61
+ * JavaScript; `\s` is JavaScript's whitespace, U+FEFF and U+3000 included.
62
+ * After an empty match `findAll`, `replaceAll` and `split` step one character.
63
+ *
64
+ * **What a call costs.** The VM's two thread lists, its capture tables and
65
+ * its stack are allocated once by `regexCompile`. `test` tracks no captures,
66
+ * `findAll` and a `replace` whose template names no group track only the
67
+ * match's bounds, and only `exec`, `find`, `split` and a template with `$1` or
68
+ * `$<name>` pay for every group. A pattern that starts with a literal is searched for with
69
+ * `s.indexOf`; one whose first few bytes are each one of a set rare enough to
70
+ * be worth it, like `[A-Z][a-z]` or `cat|dog`, is searched for those bytes by
71
+ * a runtime kernel that tests sixteen or thirty-two starts at a time; any
72
+ * other whose first characters are known skips the bytes that cannot start a
73
+ * match with a 128-entry table; and a pattern that is nothing but a literal
74
+ * never starts an engine at all. The DFA's states are
75
+ * built as searches need them and kept, so only a search that meets new
76
+ * states allocates, and only up to a bound:
77
+ * its table holds at most 2,048 states and 262,144 transitions (1 MiB), and
78
+ * a search that fills it clears it and goes on; one that keeps clearing it
79
+ * without reading at least 10 bytes per state hands the rest of the text to
80
+ * the VM, which is slower but never needs more memory. Because the scratch
81
+ * memory lives in the `Regex`, one `Regex` is used by one thread at a time.
82
+ */
83
+
84
+ import { uncheckedGet } from "nish:unsafe"
85
+
86
+ /** The most instructions a compiled pattern may have. */
87
+ export const REGEX_MAX_INSTRUCTIONS: i32 = 100000
88
+ /** The largest count `{n}`, `{n,}` or `{n,m}` may give. */
89
+ export const REGEX_MAX_REPEAT: i32 = 1000
90
+ /** How deeply groups may nest. */
91
+ export const REGEX_MAX_DEPTH: i32 = 250
92
+ /** The most entries one thread list's capture table may have: instructions × slots per thread. */
93
+ const REGEX_MAX_SLOT_TABLE: i32 = 4194304
94
+ /** The most capturing groups a pattern may have, past group 0; a name is checked against each. */
95
+ export const REGEX_MAX_GROUPS: i32 = 1024
96
+ /** The most ranges one class may list; the class sorts them by insertion. */
97
+ export const REGEX_MAX_CLASS_RANGES: i32 = 10000
98
+ /**
99
+ * How many levels of nested marked iterations the VM keeps apart; a mark
100
+ * nested deeper leaves the closure's stamps as they are.
101
+ */
102
+ const REGEX_MAX_LEVELS: i32 = 3
103
+
104
+ // Instructions. Each is an opcode with up to two operands, `ia` and `ib`.
105
+ /** Consume a code point equal to `ia` or `ib` (its other case under `i`). */
106
+ const OP_CHAR: i32 = 0
107
+ /** Consume a code point in class `ia`. */
108
+ const OP_CLASS: i32 = 1
109
+ /** Consume any code point. */
110
+ const OP_ANY: i32 = 2
111
+ /** Consume any code point but a line terminator. */
112
+ const OP_ANY_NL: i32 = 3
113
+ /** Go on at `ia` and, at lower priority, at `ib`. */
114
+ const OP_SPLIT: i32 = 4
115
+ /** Go on at `ia`. */
116
+ const OP_JMP: i32 = 5
117
+ /** Record the position in slot `ia`. */
118
+ const OP_SAVE: i32 = 6
119
+ /** Reset slots `[ia, ib)`: a quantified group's captures, at each iteration. */
120
+ const OP_CLEAR: i32 = 7
121
+ /** Go on only where assertion `ia` holds. */
122
+ const OP_ASSERT: i32 = 8
123
+ /** The pattern has matched. */
124
+ const OP_MATCH: i32 = 9
125
+ /** Record where an optional iteration starts, in slot `ia`; `ib` is how deep it nests. */
126
+ const OP_MARK: i32 = 10
127
+ /** Go on only if the iteration that slot `ia` marked consumed something. */
128
+ const OP_CHECK: i32 = 11
129
+
130
+ /**
131
+ * An operand an instruction or node does not use, or one patched later. The
132
+ * constants are named, not bare `0` and `-1`, because under `--number-mode f64`
133
+ * a bare literal argument or `push` is an `f64`.
134
+ */
135
+ const REGEX_NIL: i32 = 0
136
+ /** No node, instruction or mark: the end of a list, or a field not yet set. */
137
+ const REGEX_NONE: i32 = -1
138
+ /** Group 0, the whole match, whose bounds are slots 0 and 1. */
139
+ const REGEX_WHOLE: i32 = 0
140
+ /** The program's first instruction. */
141
+ const REGEX_ENTRY: i32 = 0
142
+ /** A text's first byte. */
143
+ const REGEX_TEXT_START: i32 = 0
144
+ /** The nesting depth of the pattern's outermost alternation. */
145
+ const REGEX_TOP: i32 = 0
146
+ /** `run` asked only whether there is a match, or for its bounds as well. */
147
+ const REGEX_WANT_NONE: i32 = 0
148
+ const REGEX_WANT_BOUNDS: i32 = 2
149
+ /** The digits of `\xHH` and of `\uHHHH`. */
150
+ const REGEX_HEX_BYTE: i32 = 2
151
+ const REGEX_HEX_UNIT: i32 = 4
152
+
153
+ const ASSERT_BEGIN_TEXT: i32 = 0
154
+ const ASSERT_END_TEXT: i32 = 1
155
+ const ASSERT_BEGIN_LINE: i32 = 2
156
+ const ASSERT_END_LINE: i32 = 3
157
+ const ASSERT_WORD: i32 = 4
158
+ const ASSERT_NOT_WORD: i32 = 5
159
+
160
+ // The parsed pattern's nodes.
161
+ const NODE_EMPTY: i32 = 0
162
+ const NODE_CHAR: i32 = 1
163
+ const NODE_CLASS: i32 = 2
164
+ const NODE_ANY: i32 = 3
165
+ const NODE_ASSERT: i32 = 4
166
+ const NODE_CAT: i32 = 5
167
+ const NODE_ALT: i32 = 6
168
+ const NODE_GROUP: i32 = 7
169
+ const NODE_REPEAT: i32 = 8
170
+
171
+ // The escapes that stand for a set: `\d`, `\w`, `\s`.
172
+ const SET_DIGIT: i32 = 0
173
+ const SET_WORD: i32 = 1
174
+ const SET_SPACE: i32 = 2
175
+
176
+ /** The highest code point. */
177
+ const REGEX_MAX_CP: i32 = 0x10ffff
178
+ /** A byte `b` that is not part of valid UTF-8 is the character `REGEX_INVALID + b`, which nothing written in a pattern equals. */
179
+ const REGEX_INVALID: i32 = 0x110000
180
+
181
+ /** The other case of an ASCII letter, and any other code point itself. */
182
+ const regexOtherCase = (c: i32): i32 => {
183
+ if (c >= 97 && c <= 122) {
184
+ return c - 32
185
+ }
186
+ if (c >= 65 && c <= 90) {
187
+ return c + 32
188
+ }
189
+ return c
190
+ }
191
+
192
+ /** Whether `b` is an ASCII word byte, `[A-Za-z0-9_]`, as `\w` and `\b` read it. */
193
+ const isRegexWordByte = (b: i32): boolean =>
194
+ (b >= 97 && b <= 122) || (b >= 65 && b <= 90) || (b >= 48 && b <= 57) || b === 95
195
+
196
+ /** Whether every character of `s` is ASCII. */
197
+ const isRegexAscii = (s: string): boolean => {
198
+ let i: i32 = 0
199
+ while (i < toI32(s.length)) {
200
+ if (toI32(s.charCodeAt(i)) >= 128) {
201
+ return false
202
+ }
203
+ i += 1
204
+ }
205
+ return true
206
+ }
207
+
208
+ /** The value of hex digit `c`, or -1. */
209
+ const regexHexValue = (c: i32): i32 => {
210
+ if (c >= 48 && c <= 57) {
211
+ return c - 48
212
+ }
213
+ if (c >= 97 && c <= 102) {
214
+ return c - 87
215
+ }
216
+ if (c >= 65 && c <= 70) {
217
+ return c - 55
218
+ }
219
+ return -1
220
+ }
221
+
222
+ /**
223
+ * The code point at byte `p` of `text`, packed as `cp * 8 + width`. `p` is
224
+ * below `len`. A byte that does not start a valid UTF-8 sequence — a stray
225
+ * continuation byte, an overlong form, a surrogate, a truncated sequence — is
226
+ * one character, `REGEX_INVALID + byte`, of width 1.
227
+ */
228
+ const regexDecodeAt = (text: string, p: i32, len: i32): i32 => {
229
+ const b0: i32 = toI32(text.charCodeAt(p))
230
+ if (b0 < 0x80) {
231
+ return b0 * 8 + 1
232
+ }
233
+ const invalid: i32 = (REGEX_INVALID + (b0 & 0xff)) * 8 + 1
234
+ if (b0 < 0xc2 || b0 > 0xf4) {
235
+ return invalid
236
+ }
237
+ if (p + 1 >= len) {
238
+ return invalid
239
+ }
240
+ const b1: i32 = toI32(text.charCodeAt(p + 1))
241
+ if ((b1 & 0xc0) !== 0x80) {
242
+ return invalid
243
+ }
244
+ if (b0 < 0xe0) {
245
+ return (((b0 & 0x1f) << 6) | (b1 & 0x3f)) * 8 + 2
246
+ }
247
+ if (
248
+ (b0 === 0xe0 && b1 < 0xa0) ||
249
+ (b0 === 0xed && b1 >= 0xa0) ||
250
+ (b0 === 0xf0 && b1 < 0x90) ||
251
+ (b0 === 0xf4 && b1 >= 0x90)
252
+ ) {
253
+ return invalid
254
+ }
255
+ if (p + 2 >= len) {
256
+ return invalid
257
+ }
258
+ const b2: i32 = toI32(text.charCodeAt(p + 2))
259
+ if ((b2 & 0xc0) !== 0x80) {
260
+ return invalid
261
+ }
262
+ if (b0 < 0xf0) {
263
+ return (((b0 & 0x0f) << 12) | ((b1 & 0x3f) << 6) | (b2 & 0x3f)) * 8 + 3
264
+ }
265
+ if (p + 3 >= len) {
266
+ return invalid
267
+ }
268
+ const b3: i32 = toI32(text.charCodeAt(p + 3))
269
+ if ((b3 & 0xc0) !== 0x80) {
270
+ return invalid
271
+ }
272
+ return (((b0 & 0x07) << 18) | ((b1 & 0x3f) << 12) | ((b2 & 0x3f) << 6) | (b3 & 0x3f)) * 8 + 4
273
+ }
274
+
275
+ /** How many bytes the character at `p` takes: 1 at or past the end. */
276
+ const regexWidthAt = (text: string, p: i32, len: i32): i32 => {
277
+ if (p >= len) {
278
+ return 1
279
+ }
280
+ return regexDecodeAt(text, p, len) & 7
281
+ }
282
+
283
+ /** Append the UTF-8 encoding of `cp` to `out`. */
284
+ const regexPushUtf8 = (out: u8[], cp: i32): void => {
285
+ if (cp < 0x80) {
286
+ out.push(toU8(cp))
287
+ } else if (cp < 0x800) {
288
+ out.push(toU8((cp >> 6) | 0xc0))
289
+ out.push(toU8((cp & 0x3f) | 0x80))
290
+ } else if (cp < 0x10000) {
291
+ out.push(toU8((cp >> 12) | 0xe0))
292
+ out.push(toU8(((cp >> 6) & 0x3f) | 0x80))
293
+ out.push(toU8((cp & 0x3f) | 0x80))
294
+ } else {
295
+ out.push(toU8((cp >> 18) | 0xf0))
296
+ out.push(toU8(((cp >> 12) & 0x3f) | 0x80))
297
+ out.push(toU8(((cp >> 6) & 0x3f) | 0x80))
298
+ out.push(toU8((cp & 0x3f) | 0x80))
299
+ }
300
+ }
301
+
302
+ /**
303
+ * The pattern read into a tree of nodes, and its classes built.
304
+ *
305
+ * A node is an index into the parallel arrays: `kind`, three operands, the
306
+ * group range a repeat clears, its first child and its next sibling. A
307
+ * class is 128 bytes of `clsAscii` — membership of each ASCII code point,
308
+ * negation already applied — and a sorted, merged run of non-ASCII ranges
309
+ * `[clsFrom, clsTo)` in `clsLo` / `clsHi`, read through `clsNeg`.
310
+ */
311
+ class RegexParser {
312
+ // Widest first, so the alignment rules insert no padding.
313
+ src: string
314
+ error: string
315
+
316
+ kind: i32[]
317
+ na: i32[]
318
+ nb: i32[]
319
+ nc: i32[]
320
+ nd: i32[]
321
+ ne: i32[]
322
+ kid: i32[]
323
+ next: i32[]
324
+
325
+ names: string[]
326
+
327
+ clsAscii: u8[]
328
+ clsLo: i32[]
329
+ clsHi: i32[]
330
+ clsFrom: i32[]
331
+ clsTo: i32[]
332
+ clsNeg: u8[]
333
+ /** The class being parsed: its ranges, unsorted, `tmpCount` of them. */
334
+ tmpLo: i32[]
335
+ tmpHi: i32[]
336
+
337
+ len: i32
338
+ pos: i32
339
+ errorAt: i32
340
+ /** Capture groups, the whole match (group 0) included. */
341
+ groups: i32
342
+ tmpCount: i32
343
+ /** What `parseBraces` read. */
344
+ qMin: i32
345
+ qMax: i32
346
+
347
+ icase: boolean
348
+ multiline: boolean
349
+ dotAll: boolean
350
+ failed: boolean
351
+ hasNames: boolean
352
+
353
+ constructor(src: string, icase: boolean, multiline: boolean, dotAll: boolean) {
354
+ this.src = src
355
+ this.len = toI32(src.length)
356
+ this.pos = 0
357
+ this.icase = icase
358
+ this.multiline = multiline
359
+ this.dotAll = dotAll
360
+ this.failed = false
361
+ this.error = ""
362
+ this.errorAt = 0
363
+ this.kind = []
364
+ this.na = []
365
+ this.nb = []
366
+ this.nc = []
367
+ this.nd = []
368
+ this.ne = []
369
+ this.kid = []
370
+ this.next = []
371
+ this.groups = 1
372
+ this.names = [""]
373
+ this.hasNames = false
374
+ this.clsAscii = []
375
+ this.clsLo = []
376
+ this.clsHi = []
377
+ this.clsFrom = []
378
+ this.clsTo = []
379
+ this.clsNeg = []
380
+ this.tmpLo = []
381
+ this.tmpHi = []
382
+ this.tmpCount = 0
383
+ this.qMin = 0
384
+ this.qMax = 0
385
+ }
386
+
387
+ fail(message: string, at: i32): i32 {
388
+ if (!this.failed) {
389
+ this.failed = true
390
+ this.error = message
391
+ this.errorAt = at
392
+ }
393
+ return -1
394
+ }
395
+
396
+ byteAt(i: i32): i32 {
397
+ return toI32(this.src.charCodeAt(i))
398
+ }
399
+
400
+ node(kind: i32, a: i32, b: i32): i32 {
401
+ const n: i32 = toI32(this.kind.length)
402
+ this.kind.push(kind)
403
+ this.na.push(a)
404
+ this.nb.push(b)
405
+ this.nc.push(REGEX_NIL)
406
+ this.nd.push(REGEX_NIL)
407
+ this.ne.push(REGEX_NIL)
408
+ this.kid.push(REGEX_NONE)
409
+ this.next.push(REGEX_NONE)
410
+ return n
411
+ }
412
+
413
+ /** A node for code point `cp`, with its other case under `i`. */
414
+ charNode(cp: i32): i32 {
415
+ if (this.icase) {
416
+ return this.node(NODE_CHAR, cp, regexOtherCase(cp))
417
+ }
418
+ return this.node(NODE_CHAR, cp, cp)
419
+ }
420
+
421
+ /** `alternative ('|' alternative)*`. */
422
+ parseAlt(depth: i32): i32 {
423
+ if (depth > REGEX_MAX_DEPTH) {
424
+ return this.fail("groups nest too deeply", this.pos)
425
+ }
426
+ const first: i32 = this.parseSeq(depth)
427
+ if (this.failed) {
428
+ return -1
429
+ }
430
+ if (!(this.pos < this.len && this.byteAt(this.pos) === 124)) {
431
+ return first
432
+ }
433
+ const alt: i32 = this.node(NODE_ALT, REGEX_NIL, REGEX_NIL)
434
+ this.kid[alt] = first
435
+ let last: i32 = first
436
+ while (this.pos < this.len && this.byteAt(this.pos) === 124) {
437
+ this.pos += 1
438
+ const n: i32 = this.parseSeq(depth)
439
+ if (this.failed) {
440
+ return -1
441
+ }
442
+ this.next[last] = n
443
+ last = n
444
+ }
445
+ return alt
446
+ }
447
+
448
+ /** The terms up to a `|`, a `)` or the end. */
449
+ parseSeq(depth: i32): i32 {
450
+ let first: i32 = -1
451
+ let last: i32 = -1
452
+ let count: i32 = 0
453
+ while (this.pos < this.len) {
454
+ const c: i32 = this.byteAt(this.pos)
455
+ if (c === 124 || c === 41) {
456
+ break
457
+ }
458
+ const t: i32 = this.parseTerm(depth)
459
+ if (this.failed) {
460
+ return -1
461
+ }
462
+ if (first === -1) {
463
+ first = t
464
+ } else {
465
+ this.next[last] = t
466
+ }
467
+ last = t
468
+ count += 1
469
+ }
470
+ if (count === 0) {
471
+ return this.node(NODE_EMPTY, REGEX_NIL, REGEX_NIL)
472
+ }
473
+ if (count === 1) {
474
+ return first
475
+ }
476
+ const cat: i32 = this.node(NODE_CAT, REGEX_NIL, REGEX_NIL)
477
+ this.kid[cat] = first
478
+ return cat
479
+ }
480
+
481
+ /** An atom and at most one quantifier. */
482
+ parseTerm(depth: i32): i32 {
483
+ const groupsBefore: i32 = this.groups
484
+ const atomAt: i32 = this.pos
485
+ const atom: i32 = this.parseAtom(depth)
486
+ if (this.failed || this.pos >= this.len) {
487
+ return atom
488
+ }
489
+ const c: i32 = this.byteAt(this.pos)
490
+ let min: i32 = 0
491
+ let max: i32 = 0
492
+ if (c === 42) {
493
+ max = -1
494
+ this.pos += 1
495
+ } else if (c === 43) {
496
+ min = 1
497
+ max = -1
498
+ this.pos += 1
499
+ } else if (c === 63) {
500
+ max = 1
501
+ this.pos += 1
502
+ } else if (c === 123 && this.parseBraces()) {
503
+ if (this.failed) {
504
+ return -1
505
+ }
506
+ min = this.qMin
507
+ max = this.qMax
508
+ } else {
509
+ return atom
510
+ }
511
+ // A bare `^`, `$`, `\b` or `\B` takes no quantifier, as in JavaScript,
512
+ // but one in a group does: `(?:^)?` is an assertion node too.
513
+ if (this.kind[atom] === NODE_ASSERT && this.byteAt(atomAt) !== 40) {
514
+ return this.fail("nothing to repeat", atomAt)
515
+ }
516
+ let greedy: i32 = 1
517
+ if (this.pos < this.len && this.byteAt(this.pos) === 63) {
518
+ greedy = 0
519
+ this.pos += 1
520
+ }
521
+ if (this.pos < this.len) {
522
+ const at: i32 = this.pos
523
+ const d: i32 = this.byteAt(at)
524
+ if (d === 42 || d === 43 || d === 63 || (d === 123 && this.parseBraces())) {
525
+ return this.fail("nothing to repeat", at)
526
+ }
527
+ }
528
+ const r: i32 = this.node(NODE_REPEAT, min, max)
529
+ this.nc[r] = greedy
530
+ this.nd[r] = groupsBefore
531
+ this.ne[r] = this.groups
532
+ this.kid[r] = atom
533
+ return r
534
+ }
535
+
536
+ /**
537
+ * At a `{`: whether `{n}`, `{n,}` or `{n,m}` follows, and if so its bounds
538
+ * in `qMin` / `qMax` (-1 unbounded) with the cursor past it. Anything else
539
+ * leaves the cursor where it was.
540
+ */
541
+ parseBraces(): boolean {
542
+ let i: i32 = this.pos + 1
543
+ const start: i32 = this.pos
544
+ let min: i32 = 0
545
+ let digits: i32 = 0
546
+ while (i < this.len && this.byteAt(i) >= 48 && this.byteAt(i) <= 57) {
547
+ if (min <= REGEX_MAX_REPEAT) {
548
+ min = min * 10 + (this.byteAt(i) - 48)
549
+ }
550
+ digits += 1
551
+ i += 1
552
+ }
553
+ if (digits === 0 || i >= this.len) {
554
+ return false
555
+ }
556
+ let max: i32 = min
557
+ if (this.byteAt(i) === 44) {
558
+ i += 1
559
+ max = -1
560
+ let maxDigits: i32 = 0
561
+ let m: i32 = 0
562
+ while (i < this.len && this.byteAt(i) >= 48 && this.byteAt(i) <= 57) {
563
+ if (m <= REGEX_MAX_REPEAT) {
564
+ m = m * 10 + (this.byteAt(i) - 48)
565
+ }
566
+ maxDigits += 1
567
+ i += 1
568
+ }
569
+ if (maxDigits > 0) {
570
+ max = m
571
+ }
572
+ }
573
+ if (i >= this.len || this.byteAt(i) !== 125) {
574
+ return false
575
+ }
576
+ this.pos = i + 1
577
+ if (min > REGEX_MAX_REPEAT || max > REGEX_MAX_REPEAT) {
578
+ this.fail("a repeat count is over 1000", start)
579
+ return true
580
+ }
581
+ if (max !== -1 && max < min) {
582
+ this.fail("numbers out of order in {} quantifier", start)
583
+ return true
584
+ }
585
+ this.qMin = min
586
+ this.qMax = max
587
+ return true
588
+ }
589
+
590
+ parseAtom(depth: i32): i32 {
591
+ const c: i32 = this.byteAt(this.pos)
592
+ if (c === 40) {
593
+ return this.parseGroup(depth)
594
+ }
595
+ if (c === 91) {
596
+ return this.parseClass()
597
+ }
598
+ if (c === 46) {
599
+ this.pos += 1
600
+ return this.node(NODE_ANY, REGEX_NIL, REGEX_NIL)
601
+ }
602
+ if (c === 94) {
603
+ this.pos += 1
604
+ return this.node(NODE_ASSERT, this.multiline ? ASSERT_BEGIN_LINE : ASSERT_BEGIN_TEXT, REGEX_NIL)
605
+ }
606
+ if (c === 36) {
607
+ this.pos += 1
608
+ return this.node(NODE_ASSERT, this.multiline ? ASSERT_END_LINE : ASSERT_END_TEXT, REGEX_NIL)
609
+ }
610
+ if (c === 92) {
611
+ return this.parseEscape()
612
+ }
613
+ if (c === 42 || c === 43 || c === 63) {
614
+ return this.fail("nothing to repeat", this.pos)
615
+ }
616
+ if (c === 123) {
617
+ const at: i32 = this.pos
618
+ if (this.parseBraces()) {
619
+ return this.fail("nothing to repeat", at)
620
+ }
621
+ return this.fail("lone quantifier bracket", at)
622
+ }
623
+ if (c === 125) {
624
+ return this.fail("lone quantifier bracket", this.pos)
625
+ }
626
+ if (c === 93) {
627
+ return this.fail("lone ]", this.pos)
628
+ }
629
+ const packed: i32 = regexDecodeAt(this.src, this.pos, this.len)
630
+ if (packed >> 3 >= REGEX_INVALID) {
631
+ return this.fail("the pattern is not valid UTF-8", this.pos)
632
+ }
633
+ this.pos += packed & 7
634
+ return this.charNode(packed >> 3)
635
+ }
636
+
637
+ parseGroup(depth: i32): i32 {
638
+ const open: i32 = this.pos
639
+ this.pos += 1
640
+ let capture: boolean = true
641
+ let name: string = ""
642
+ if (this.pos < this.len && this.byteAt(this.pos) === 63) {
643
+ const k: i32 = this.pos + 1 < this.len ? this.byteAt(this.pos + 1) : -1
644
+ if (k === 58) {
645
+ capture = false
646
+ this.pos += 2
647
+ } else if (k === 61 || k === 33) {
648
+ return this.fail("lookahead is not supported", open)
649
+ } else if (k === 60) {
650
+ const k2: i32 = this.pos + 2 < this.len ? this.byteAt(this.pos + 2) : -1
651
+ if (k2 === 61 || k2 === 33) {
652
+ return this.fail("lookbehind is not supported", open)
653
+ }
654
+ const nameAt: i32 = this.pos + 2
655
+ let i: i32 = nameAt
656
+ while (i < this.len && this.byteAt(i) !== 62) {
657
+ const b: i32 = this.byteAt(i)
658
+ const first: boolean = (b >= 97 && b <= 122) || (b >= 65 && b <= 90) || b === 95 || b === 36
659
+ if (!(first || (i > nameAt && b >= 48 && b <= 57))) {
660
+ return this.fail("invalid group name", nameAt)
661
+ }
662
+ i += 1
663
+ }
664
+ if (i >= this.len || i === nameAt) {
665
+ return this.fail("invalid group name", nameAt)
666
+ }
667
+ name = this.src.slice(nameAt, i)
668
+ if (this.names.indexOf(name) >= 0) {
669
+ return this.fail("duplicate group name", nameAt)
670
+ }
671
+ this.pos = i + 1
672
+ } else {
673
+ return this.fail("unsupported group syntax", open)
674
+ }
675
+ }
676
+ let index: i32 = 0
677
+ if (capture) {
678
+ if (this.groups > REGEX_MAX_GROUPS) {
679
+ return this.fail("too many groups", open)
680
+ }
681
+ index = this.groups
682
+ this.groups += 1
683
+ this.names.push(name)
684
+ if (name !== "") {
685
+ this.hasNames = true
686
+ }
687
+ }
688
+ const inner: i32 = this.parseAlt(depth + 1)
689
+ if (this.failed) {
690
+ return -1
691
+ }
692
+ if (this.pos >= this.len || this.byteAt(this.pos) !== 41) {
693
+ return this.fail("missing )", open)
694
+ }
695
+ this.pos += 1
696
+ if (!capture) {
697
+ return inner
698
+ }
699
+ const g: i32 = this.node(NODE_GROUP, index, REGEX_NIL)
700
+ this.kid[g] = inner
701
+ return g
702
+ }
703
+
704
+ /** A `\` outside a class. */
705
+ parseEscape(): i32 {
706
+ const at: i32 = this.pos
707
+ this.pos += 1
708
+ if (this.pos >= this.len) {
709
+ return this.fail("\\ at end of pattern", at)
710
+ }
711
+ const c: i32 = this.byteAt(this.pos)
712
+ if (c === 100 || c === 68 || c === 119 || c === 87 || c === 115 || c === 83) {
713
+ this.pos += 1
714
+ this.tmpCount = 0
715
+ this.addSet(c)
716
+ return this.finishClass(false)
717
+ }
718
+ if (c === 98) {
719
+ this.pos += 1
720
+ return this.node(NODE_ASSERT, ASSERT_WORD, REGEX_NIL)
721
+ }
722
+ if (c === 66) {
723
+ this.pos += 1
724
+ return this.node(NODE_ASSERT, ASSERT_NOT_WORD, REGEX_NIL)
725
+ }
726
+ if ((c >= 49 && c <= 57) || c === 107) {
727
+ return this.fail("backreferences are not supported", at)
728
+ }
729
+ const cp: i32 = this.parseCharEscape(false)
730
+ if (this.failed) {
731
+ return -1
732
+ }
733
+ return this.charNode(cp)
734
+ }
735
+
736
+ /**
737
+ * The code point an escape stands for, the cursor on the byte after the
738
+ * `\`. Shared by escapes inside and outside a class; inside one, `\b` is a
739
+ * backspace and `\-` a hyphen.
740
+ */
741
+ parseCharEscape(inClass: boolean): i32 {
742
+ const at: i32 = this.pos - 1
743
+ const c: i32 = this.byteAt(this.pos)
744
+ this.pos += 1
745
+ if (c === 110) {
746
+ return 10
747
+ }
748
+ if (c === 114) {
749
+ return 13
750
+ }
751
+ if (c === 116) {
752
+ return 9
753
+ }
754
+ if (c === 102) {
755
+ return 12
756
+ }
757
+ if (c === 118) {
758
+ return 11
759
+ }
760
+ if (c === 48) {
761
+ if (this.pos < this.len && this.byteAt(this.pos) >= 48 && this.byteAt(this.pos) <= 57) {
762
+ return this.fail("octal escapes are not supported", at)
763
+ }
764
+ return 0
765
+ }
766
+ if (c === 99) {
767
+ if (this.pos < this.len) {
768
+ const l: i32 = this.byteAt(this.pos)
769
+ if ((l >= 97 && l <= 122) || (l >= 65 && l <= 90)) {
770
+ this.pos += 1
771
+ return l % 32
772
+ }
773
+ }
774
+ return this.fail("invalid \\c escape", at)
775
+ }
776
+ if (c === 120) {
777
+ const v: i32 = this.parseHex(REGEX_HEX_BYTE)
778
+ if (v < 0) {
779
+ return this.fail("invalid \\x escape", at)
780
+ }
781
+ return v
782
+ }
783
+ if (c === 117) {
784
+ return this.parseUnicodeEscape(at)
785
+ }
786
+ if (inClass && c === 98) {
787
+ return 8
788
+ }
789
+ if (inClass && c === 45) {
790
+ return 45
791
+ }
792
+ // ^ $ \ . * + ? ( ) [ ] { } | /
793
+ if (
794
+ c === 94 ||
795
+ c === 36 ||
796
+ c === 92 ||
797
+ c === 46 ||
798
+ c === 42 ||
799
+ c === 43 ||
800
+ c === 63 ||
801
+ c === 40 ||
802
+ c === 41 ||
803
+ c === 91 ||
804
+ c === 93 ||
805
+ c === 123 ||
806
+ c === 125 ||
807
+ c === 124 ||
808
+ c === 47
809
+ ) {
810
+ return c
811
+ }
812
+ return this.fail("invalid escape", at)
813
+ }
814
+
815
+ /** Exactly `n` hex digits at the cursor, or -1 with the cursor left. */
816
+ parseHex(n: i32): i32 {
817
+ if (this.pos + n > this.len) {
818
+ return -1
819
+ }
820
+ let v: i32 = 0
821
+ let i: i32 = 0
822
+ while (i < n) {
823
+ const h: i32 = regexHexValue(this.byteAt(this.pos + i))
824
+ if (h < 0) {
825
+ return -1
826
+ }
827
+ v = v * 16 + h
828
+ i += 1
829
+ }
830
+ this.pos += n
831
+ return v
832
+ }
833
+
834
+ /** `\uHHHH`, a surrogate pair of them, or `\u{H…}`, the cursor after the `u`. */
835
+ parseUnicodeEscape(at: i32): i32 {
836
+ if (this.pos < this.len && this.byteAt(this.pos) === 123) {
837
+ let i: i32 = this.pos + 1
838
+ let v: i32 = 0
839
+ while (i < this.len && regexHexValue(this.byteAt(i)) >= 0) {
840
+ v = v * 16 + regexHexValue(this.byteAt(i))
841
+ if (v > REGEX_MAX_CP) {
842
+ return this.fail("invalid \\u escape", at)
843
+ }
844
+ i += 1
845
+ }
846
+ if (i === this.pos + 1 || i >= this.len || this.byteAt(i) !== 125) {
847
+ return this.fail("invalid \\u escape", at)
848
+ }
849
+ this.pos = i + 1
850
+ return v
851
+ }
852
+ const v: i32 = this.parseHex(REGEX_HEX_UNIT)
853
+ if (v < 0) {
854
+ return this.fail("invalid \\u escape", at)
855
+ }
856
+ if (
857
+ v >= 0xd800 &&
858
+ v <= 0xdbff &&
859
+ this.pos + 6 <= this.len &&
860
+ this.byteAt(this.pos) === 92 &&
861
+ this.byteAt(this.pos + 1) === 117
862
+ ) {
863
+ const save: i32 = this.pos
864
+ this.pos += 2
865
+ const lo: i32 = this.parseHex(REGEX_HEX_UNIT)
866
+ if (lo >= 0xdc00 && lo <= 0xdfff) {
867
+ return ((v - 0xd800) << 10) + (lo - 0xdc00) + 0x10000
868
+ }
869
+ this.pos = save
870
+ }
871
+ return v
872
+ }
873
+
874
+ addRange(lo: i32, hi: i32): void {
875
+ if (this.tmpCount < toI32(this.tmpLo.length)) {
876
+ this.tmpLo[this.tmpCount] = lo
877
+ this.tmpHi[this.tmpCount] = hi
878
+ } else {
879
+ this.tmpLo.push(lo)
880
+ this.tmpHi.push(hi)
881
+ }
882
+ this.tmpCount += 1
883
+ }
884
+
885
+ /**
886
+ * Add the ranges of `\d`, `\w`, `\s` or their capitals — `letter` is the
887
+ * escape's letter — to the class being parsed. A capital adds the complement, the
888
+ * gaps between the set's sorted ranges.
889
+ */
890
+ addSet(letter: i32): void {
891
+ const negate: boolean = letter === 68 || letter === 87 || letter === 83
892
+ const lower: i32 = negate ? letter + 32 : letter
893
+ let set: i32 = SET_SPACE
894
+ if (lower === 100) {
895
+ set = SET_DIGIT
896
+ } else if (lower === 119) {
897
+ set = SET_WORD
898
+ }
899
+ // The set's ranges, in order, as lo/hi pairs.
900
+ const r: i32[] = []
901
+ if (set === SET_DIGIT) {
902
+ r.push(48)
903
+ r.push(57)
904
+ } else if (set === SET_WORD) {
905
+ r.push(48)
906
+ r.push(57)
907
+ r.push(65)
908
+ r.push(90)
909
+ r.push(95)
910
+ r.push(95)
911
+ r.push(97)
912
+ r.push(122)
913
+ } else {
914
+ r.push(9)
915
+ r.push(13)
916
+ r.push(32)
917
+ r.push(32)
918
+ r.push(0xa0)
919
+ r.push(0xa0)
920
+ r.push(0x1680)
921
+ r.push(0x1680)
922
+ r.push(0x2000)
923
+ r.push(0x200a)
924
+ r.push(0x2028)
925
+ r.push(0x2029)
926
+ r.push(0x202f)
927
+ r.push(0x202f)
928
+ r.push(0x205f)
929
+ r.push(0x205f)
930
+ r.push(0x3000)
931
+ r.push(0x3000)
932
+ r.push(0xfeff)
933
+ r.push(0xfeff)
934
+ }
935
+ if (!negate) {
936
+ let i: i32 = 0
937
+ while (i >= 0 && i + 1 < toI32(r.length)) {
938
+ this.addRange(r[i], r[i + 1])
939
+ i += 2
940
+ }
941
+ return
942
+ }
943
+ let from: i32 = 0
944
+ let i: i32 = 0
945
+ while (i >= 0 && i + 1 < toI32(r.length)) {
946
+ const lo: i32 = r[i]
947
+ const hi: i32 = r[i + 1]
948
+ if (lo > from) {
949
+ this.addRange(from, lo - 1)
950
+ }
951
+ from = hi + 1
952
+ i += 2
953
+ }
954
+ if (from <= REGEX_MAX_CP) {
955
+ this.addRange(from, REGEX_MAX_CP)
956
+ }
957
+ }
958
+
959
+ /** One member of a class: a code point, or -2 once a set escape has added its ranges. */
960
+ parseClassAtom(): i32 {
961
+ const c: i32 = this.byteAt(this.pos)
962
+ if (c === 92) {
963
+ const at: i32 = this.pos
964
+ this.pos += 1
965
+ if (this.pos >= this.len) {
966
+ return this.fail("\\ at end of pattern", at)
967
+ }
968
+ const e: i32 = this.byteAt(this.pos)
969
+ if (e === 100 || e === 68 || e === 119 || e === 87 || e === 115 || e === 83) {
970
+ this.pos += 1
971
+ this.addSet(e)
972
+ return -2
973
+ }
974
+ if ((e >= 49 && e <= 57) || e === 107) {
975
+ return this.fail("backreferences are not supported", at)
976
+ }
977
+ return this.parseCharEscape(true)
978
+ }
979
+ const packed: i32 = regexDecodeAt(this.src, this.pos, this.len)
980
+ if (packed >> 3 >= REGEX_INVALID) {
981
+ return this.fail("the pattern is not valid UTF-8", this.pos)
982
+ }
983
+ this.pos += packed & 7
984
+ return packed >> 3
985
+ }
986
+
987
+ /** `[…]` or `[^…]`. */
988
+ parseClass(): i32 {
989
+ const open: i32 = this.pos
990
+ this.pos += 1
991
+ let negated: boolean = false
992
+ if (this.pos < this.len && this.byteAt(this.pos) === 94) {
993
+ negated = true
994
+ this.pos += 1
995
+ }
996
+ this.tmpCount = 0
997
+ while (true) {
998
+ if (this.pos >= this.len) {
999
+ return this.fail("missing ]", open)
1000
+ }
1001
+ if (this.byteAt(this.pos) === 93) {
1002
+ this.pos += 1
1003
+ break
1004
+ }
1005
+ const atAtom: i32 = this.pos
1006
+ const lo: i32 = this.parseClassAtom()
1007
+ if (this.failed) {
1008
+ return -1
1009
+ }
1010
+ const dash: boolean =
1011
+ this.pos + 1 < this.len && this.byteAt(this.pos) === 45 && this.byteAt(this.pos + 1) !== 93
1012
+ if (!dash) {
1013
+ if (lo >= 0) {
1014
+ this.addRange(lo, lo)
1015
+ }
1016
+ continue
1017
+ }
1018
+ if (lo === -2) {
1019
+ return this.fail("invalid class range", atAtom)
1020
+ }
1021
+ this.pos += 1
1022
+ const hi: i32 = this.parseClassAtom()
1023
+ if (this.failed) {
1024
+ return -1
1025
+ }
1026
+ if (hi === -2) {
1027
+ return this.fail("invalid class range", atAtom)
1028
+ }
1029
+ if (hi < lo) {
1030
+ return this.fail("class range out of order", atAtom)
1031
+ }
1032
+ this.addRange(lo, hi)
1033
+ }
1034
+ if (this.tmpCount > REGEX_MAX_CLASS_RANGES) {
1035
+ return this.fail("a class has more than 10000 ranges", open)
1036
+ }
1037
+ // A class of one code point, unnegated, is that code point.
1038
+ if (!negated && this.tmpCount === 1 && this.tmpLo[0] === this.tmpHi[0]) {
1039
+ return this.charNode(this.tmpLo[0])
1040
+ }
1041
+ return this.finishClass(negated)
1042
+ }
1043
+
1044
+ /** Store the class in `tmpLo` / `tmpHi` and answer its node. */
1045
+ finishClass(negated: boolean): i32 {
1046
+ const k: i32 = toI32(this.clsFrom.length)
1047
+ const base: i32 = toI32(this.clsAscii.length)
1048
+ let j: i32 = 0
1049
+ while (j < 128) {
1050
+ this.clsAscii.push(toU8(0))
1051
+ j += 1
1052
+ }
1053
+ let i: i32 = 0
1054
+ while (i < this.tmpCount) {
1055
+ const lo: i32 = this.tmpLo[i]
1056
+ const hi: i32 = this.tmpHi[i]
1057
+ let c: i32 = lo
1058
+ while (c <= hi && c < 128) {
1059
+ this.clsAscii[base + c] = 1
1060
+ c += 1
1061
+ }
1062
+ i += 1
1063
+ }
1064
+ if (this.icase) {
1065
+ let c: i32 = 65
1066
+ while (c <= 90) {
1067
+ if (this.clsAscii[base + c] === 1 || this.clsAscii[base + c + 32] === 1) {
1068
+ this.clsAscii[base + c] = 1
1069
+ this.clsAscii[base + c + 32] = 1
1070
+ }
1071
+ c += 1
1072
+ }
1073
+ }
1074
+ if (negated) {
1075
+ let c: i32 = 0
1076
+ while (c < 128) {
1077
+ this.clsAscii[base + c] = this.clsAscii[base + c] === 1 ? 0 : 1
1078
+ c += 1
1079
+ }
1080
+ }
1081
+ // The non-ASCII ranges, insertion-sorted by their start and merged.
1082
+ const from: i32 = toI32(this.clsLo.length)
1083
+ i = 0
1084
+ while (i < this.tmpCount) {
1085
+ let lo: i32 = this.tmpLo[i]
1086
+ const hi: i32 = this.tmpHi[i]
1087
+ i += 1
1088
+ if (hi < 128) {
1089
+ continue
1090
+ }
1091
+ if (lo < 128) {
1092
+ lo = 128
1093
+ }
1094
+ this.clsLo.push(lo)
1095
+ this.clsHi.push(hi)
1096
+ let m: i32 = toI32(this.clsLo.length) - 1
1097
+ while (m > from && this.clsLo[m - 1] > lo) {
1098
+ this.clsLo[m] = this.clsLo[m - 1]
1099
+ this.clsHi[m] = this.clsHi[m - 1]
1100
+ m -= 1
1101
+ }
1102
+ this.clsLo[m] = lo
1103
+ this.clsHi[m] = hi
1104
+ }
1105
+ // Merge in place, then drop what merging left past the end by copying
1106
+ // the merged run back over a fresh tail.
1107
+ const end: i32 = toI32(this.clsLo.length)
1108
+ let out: i32 = from
1109
+ i = from
1110
+ while (i < end) {
1111
+ const lo: i32 = this.clsLo[i]
1112
+ const hi: i32 = this.clsHi[i]
1113
+ if (out > from && lo <= this.clsHi[out - 1] + 1) {
1114
+ if (hi > this.clsHi[out - 1]) {
1115
+ this.clsHi[out - 1] = hi
1116
+ }
1117
+ } else {
1118
+ this.clsLo[out] = lo
1119
+ this.clsHi[out] = hi
1120
+ out += 1
1121
+ }
1122
+ i += 1
1123
+ }
1124
+ this.clsFrom.push(from)
1125
+ this.clsTo.push(out)
1126
+ this.clsNeg.push(negated ? toU8(1) : toU8(0))
1127
+ // The merged ranges past `out` are dead; later classes start after them.
1128
+ return this.node(NODE_CLASS, k, REGEX_NIL)
1129
+ }
1130
+ }
1131
+
1132
+ /** Whether node `n` can match without consuming anything. */
1133
+ const regexNullable = (p: RegexParser, n: i32): boolean => {
1134
+ const kind: i32 = p.kind[n]
1135
+ if (kind === NODE_CHAR || kind === NODE_CLASS || kind === NODE_ANY) {
1136
+ return false
1137
+ }
1138
+ if (kind === NODE_GROUP) {
1139
+ return regexNullable(p, p.kid[n])
1140
+ }
1141
+ if (kind === NODE_REPEAT) {
1142
+ return p.na[n] === 0 || regexNullable(p, p.kid[n])
1143
+ }
1144
+ if (kind === NODE_CAT || kind === NODE_ALT) {
1145
+ const all: boolean = kind === NODE_CAT
1146
+ let c: i32 = p.kid[n]
1147
+ while (c !== -1) {
1148
+ if (regexNullable(p, c) !== all) {
1149
+ return !all
1150
+ }
1151
+ c = p.next[c]
1152
+ }
1153
+ return all
1154
+ }
1155
+ // NODE_EMPTY and NODE_ASSERT.
1156
+ return true
1157
+ }
1158
+
1159
+ // The lazy DFA. A state is a list of instructions waiting for a character, in
1160
+ // priority order, with flags: whether a match may still start at the next
1161
+ // byte (`DFA_RESTART`), the kind of character just read and whether it runs
1162
+ // the reverse program. A transition is one i32, 0 until it is computed. A
1163
+ // plain one is the next state's offset itself, which is never 0 because the
1164
+ // first row is never a state, so the hot loop's chain from one byte to the
1165
+ // next is a load and an `|`. Any other is `-(next << 4 | flags)`, with
1166
+ // `DFA_MATCH` when a match ends before the character, `DFA_DEAD` when no
1167
+ // thread survives it and `DFA_IDLE` when the next state waits for a match to
1168
+ // start and a prefilter can find one. The hot loop follows a plain entry and
1169
+ // one with only `DFA_MATCH`, and leaves on any other.
1170
+ const DFA_MATCH: i32 = 1
1171
+ const DFA_DEAD: i32 = 2
1172
+ const DFA_IDLE: i32 = 4
1173
+ const DFA_KNOWN: i32 = 8
1174
+ const DFA_RESTART: i32 = 1
1175
+ const DFA_REV: i32 = 16
1176
+ const DFA_UNANCHORED: i32 = 0
1177
+ const DFA_ANCHORED: i32 = 1
1178
+ const DFA_REVERSE: i32 = 2
1179
+ const DFA_INNER: i32 = 3
1180
+ /** A search that finds no match. */
1181
+ const DFA_NONE: i32 = -1
1182
+ /** A search that built too many states for the bytes it read, and leaves the rest to the VM. */
1183
+ const DFA_GAVE_UP: i32 = -2
1184
+ /** At most this many units are refined into byte classes, and this much work spent doing it. */
1185
+ const DFA_MAX_UNITS: i32 = 1048576
1186
+ const DFA_MAX_WORK: i32 = 4194304
1187
+ /** At most 2^12 classes, so a state's row is at most 4096 transitions. */
1188
+ const DFA_MAX_SHIFT: i32 = 12
1189
+ const DFA_MAX_STATES: i32 = 2048
1190
+ const DFA_MAX_TABLE: i32 = 262144
1191
+ const DFA_FIRST_STATES: i32 = 64
1192
+ const DFA_FIRST_POOL: i32 = 1024
1193
+ const DFA_MIN_POOL: i32 = 16384
1194
+ const DFA_HASH_MASK: i32 = 1048575
1195
+ /** A cache cleared more than this often must read 10 bytes per state it can hold, or the search gives up. */
1196
+ const DFA_FREE_FLUSHES: i32 = 3
1197
+ const DFA_BYTES_PER_STATE: i32 = 10
1198
+ const DFA_EPOCH_MASK: i32 = 1073741823
1199
+ /** How far the inner search may read in vain before it gives way to the plain one. */
1200
+ const DFA_INNER_SLACK: f64 = 65536.0
1201
+
1202
+ // The kind of a character, as the assertions read it. `CTX_START` stands
1203
+ // before the text (or after it, read in reverse) and `CTX_EOT` after it.
1204
+ const CTX_START: i32 = 0
1205
+ const CTX_WORD: i32 = 1
1206
+ const CTX_LINE: i32 = 2
1207
+ const CTX_OTHER: i32 = 3
1208
+ const CTX_EOT: i32 = 4
1209
+ const CTX_KINDS: i32 = 5
1210
+ const CTX_SLOTS: i32 = 4
1211
+
1212
+ /**
1213
+ * Which kinds a program's assertions tell apart, as a map from each kind to
1214
+ * the one a state records. `bits` has bit `1 << kind` for every assertion
1215
+ * kind present; a pattern with none keeps one context, so it builds no state
1216
+ * twice.
1217
+ */
1218
+ const regexContextMask = (bits: i32): i32[] => {
1219
+ const m: i32[] = new Array<i32>(CTX_KINDS)
1220
+ const line: boolean = (bits & (1 << ASSERT_BEGIN_LINE)) !== 0
1221
+ if ((bits & (1 << ASSERT_BEGIN_TEXT)) !== 0) {
1222
+ m[CTX_START] = CTX_START
1223
+ } else if (line) {
1224
+ m[CTX_START] = CTX_LINE
1225
+ } else {
1226
+ m[CTX_START] = CTX_OTHER
1227
+ }
1228
+ const word: boolean = (bits & ((1 << ASSERT_WORD) | (1 << ASSERT_NOT_WORD))) !== 0
1229
+ m[CTX_WORD] = word ? CTX_WORD : CTX_OTHER
1230
+ m[CTX_LINE] = line ? CTX_LINE : CTX_OTHER
1231
+ m[CTX_OTHER] = CTX_OTHER
1232
+ m[CTX_EOT] = CTX_OTHER
1233
+ return m
1234
+ }
1235
+
1236
+ /** Sort `a` ascending, a bottom-up merge sort. */
1237
+ const regexSortPoints = (a: i32[]): void => {
1238
+ const n: i32 = toI32(a.length)
1239
+ const tmp: i32[] = new Array<i32>(n)
1240
+ let width: i32 = 1
1241
+ while (width < n) {
1242
+ let lo: i32 = 0
1243
+ while (lo < n) {
1244
+ const mid: i32 = n - lo > width ? lo + width : n
1245
+ const hi: i32 = n - mid > width ? mid + width : n
1246
+ let i: i32 = lo
1247
+ let j: i32 = mid
1248
+ let k: i32 = lo
1249
+ // Each index is also tested against the length it reads, `n` for `a`:
1250
+ // the runs end inside both arrays, and that test is what proves it.
1251
+ while (k >= 0 && k < hi && k < toI32(tmp.length)) {
1252
+ const right: boolean = j >= 0 && j < hi && j < n && (i >= mid || i < 0 || i >= n || a[j] < a[i])
1253
+ const from: i32 = right ? j : i
1254
+ if (from >= 0 && from < n) {
1255
+ tmp[k] = a[from]
1256
+ }
1257
+ if (right) {
1258
+ j += 1
1259
+ } else {
1260
+ i += 1
1261
+ }
1262
+ k += 1
1263
+ }
1264
+ lo = hi
1265
+ }
1266
+ let k: i32 = 0
1267
+ while (k < n && k < toI32(tmp.length)) {
1268
+ a[k] = tmp[k]
1269
+ k += 1
1270
+ }
1271
+ width *= 2
1272
+ }
1273
+ }
1274
+
1275
+ /** English letters, most frequent first. */
1276
+ const REGEX_LETTER_ORDER: string = "etaoinshrdlcumwfgypbvkjxqz"
1277
+
1278
+ /** How common ASCII byte `b` is in text, roughly: a prefix is searched for by its rarest byte. */
1279
+ const regexByteScore = (b: i32): i32 => {
1280
+ if (b === 32) {
1281
+ return 100
1282
+ }
1283
+ if (b >= 97 && b <= 122) {
1284
+ let i: i32 = 0
1285
+ while (i < 26) {
1286
+ if (toI32(REGEX_LETTER_ORDER.charCodeAt(i)) === b) {
1287
+ return 95 - i
1288
+ }
1289
+ i += 1
1290
+ }
1291
+ }
1292
+ if (b === 46 || b === 44) {
1293
+ return 50
1294
+ }
1295
+ if (b === 10) {
1296
+ return 45
1297
+ }
1298
+ if (b >= 65 && b <= 90) {
1299
+ return 40
1300
+ }
1301
+ if (b >= 48 && b <= 57) {
1302
+ return 30
1303
+ }
1304
+ return 10
1305
+ }
1306
+
1307
+ /** `REGEX_LETTER_ORDER`'s letters as per-mille of English text, two decimal digits each. */
1308
+ const REGEX_LETTER_PER_MILLE: string = "9570626055555048463331222219181716151411080602010101"
1309
+
1310
+ /** How many of every thousand bytes of English text the `i`th letter of `REGEX_LETTER_ORDER` is. */
1311
+ const regexLetterPerMille = (i: i32): i32 => {
1312
+ const tens: i32 = toI32(REGEX_LETTER_PER_MILLE.charCodeAt(2 * i)) - 48
1313
+ return tens * 10 + toI32(REGEX_LETTER_PER_MILLE.charCodeAt(2 * i + 1)) - 48
1314
+ }
1315
+
1316
+ /** How many of every thousand bytes of text are ASCII byte `b`, roughly: what a scan window is chosen by. */
1317
+ const regexBytePerMille = (b: i32): i32 => {
1318
+ if (b === 32) {
1319
+ return 170
1320
+ }
1321
+ if (b >= 97 && b <= 122) {
1322
+ let i: i32 = 0
1323
+ while (i < 26) {
1324
+ if (toI32(REGEX_LETTER_ORDER.charCodeAt(i)) === b) {
1325
+ return regexLetterPerMille(i)
1326
+ }
1327
+ i += 1
1328
+ }
1329
+ }
1330
+ if (b === 46 || b === 44) {
1331
+ return 8
1332
+ }
1333
+ if (b === 10) {
1334
+ return 15
1335
+ }
1336
+ if ((b >= 65 && b <= 90) || (b >= 48 && b <= 57)) {
1337
+ return 2
1338
+ }
1339
+ return 1
1340
+ }
1341
+
1342
+ /** How many leading byte positions of a match `findScan` works out the sets of. */
1343
+ const REGEX_SCAN_DEPTH: i32 = 8
1344
+ /** The most of them the runtime's scan tests at once; its contract allows 4. */
1345
+ const REGEX_SCAN_WIDTH: i32 = 3
1346
+
1347
+ /**
1348
+ * The scan's walk: the first index at or after `from` where each of the
1349
+ * `depth` bytes from it is in its position's set, or -1. `masks` holds each
1350
+ * set twice, as `runtime/nish.h` says `nish_regex_scan` reads them: 32 bytes a
1351
+ * position for the vector window, then a table of 256 a position, from
1352
+ * `32 * depth`, which is the one read here. Under Node, where a character
1353
+ * above ASCII is a code unit rather than bytes, any such unit reads as 0xFF,
1354
+ * which is in a set exactly when every byte above ASCII is. The `width` positions from `off`
1355
+ * are tested first, as the kernel looks for them first; the answer does not
1356
+ * depend on which they are.
1357
+ *
1358
+ * This loop is what runs under Node. Natively the compiler replaces the calls
1359
+ * to it with that kernel (`src/kernels.ts`), as it does `nish/text`'s
1360
+ * `indexOfAnyFrom`. Every caller hands it a `from` in `[0, length]`, a depth
1361
+ * of 1 to `REGEX_SCAN_DEPTH`, a window of 1 to `REGEX_SCAN_WIDTH` positions
1362
+ * inside it and a `masks` 288 bytes a position long.
1363
+ */
1364
+ const regexScanFrom = (text: string, masks: u8[], depth: i32, off: i32, width: i32, from: i32): i32 => {
1365
+ const length: i32 = toI32(text.length)
1366
+ let i: i32 = from
1367
+ while (i >= 0 && i <= length - depth) {
1368
+ let k: i32 = 0
1369
+ while (k < depth) {
1370
+ // The window's positions, then the ones before it, then the ones after.
1371
+ let j: i32 = k
1372
+ if (k < width) {
1373
+ j = off + k
1374
+ } else if (k < off + width) {
1375
+ j = k - width
1376
+ }
1377
+ let b: i32 = toI32(text.charCodeAt(i + j))
1378
+ if (b > 255) {
1379
+ b = 255
1380
+ }
1381
+ const slot: i32 = 32 * depth + (j << 8) + b
1382
+ // Outside the table is never reached; the test is what proves `slot`.
1383
+ if (slot < 0 || slot >= toI32(masks.length) || masks[slot] === 0) {
1384
+ break
1385
+ }
1386
+ k += 1
1387
+ }
1388
+ if (k === depth) {
1389
+ return i
1390
+ }
1391
+ i += 1
1392
+ }
1393
+ return -1
1394
+ }
1395
+
1396
+ /** One match: the input it was found in and the byte offsets of every group. */
1397
+ export class RegexMatch {
1398
+ /** The text that was searched. */
1399
+ input: string
1400
+ /** `start, end` of group 0, then of each group in order; -1 for a group that took no part. */
1401
+ slots: i32[]
1402
+ /** Each group's name, "" for an unnamed one. */
1403
+ names: string[]
1404
+
1405
+ constructor() {
1406
+ this.input = ""
1407
+ this.slots = []
1408
+ this.names = []
1409
+ }
1410
+
1411
+ /** Where the match starts. */
1412
+ start(): i32 {
1413
+ return this.slots[0]
1414
+ }
1415
+
1416
+ /** Where the match ends, one past its last byte. */
1417
+ end(): i32 {
1418
+ return this.slots[1]
1419
+ }
1420
+
1421
+ /** The matched text. */
1422
+ text(): string {
1423
+ return this.group(REGEX_WHOLE)
1424
+ }
1425
+
1426
+ /** How many groups there are, the whole match (group 0) included. */
1427
+ groupCount(): i32 {
1428
+ return toI32(this.slots.length) / 2
1429
+ }
1430
+
1431
+ /** Whether group `k` took part in the match. */
1432
+ matched(k: i32): boolean {
1433
+ return k >= 0 && k < this.groupCount() && this.slots[2 * k] >= 0
1434
+ }
1435
+
1436
+ /** Where group `k` starts, or -1. */
1437
+ groupStart(k: i32): i32 {
1438
+ return this.matched(k) ? this.slots[2 * k] : -1
1439
+ }
1440
+
1441
+ /** Where group `k` ends, or -1. */
1442
+ groupEnd(k: i32): i32 {
1443
+ return this.matched(k) ? this.slots[2 * k + 1] : -1
1444
+ }
1445
+
1446
+ /** Group `k`'s text: "" when it took no part, so test `matched` to tell the two apart. */
1447
+ group(k: i32): string {
1448
+ if (!this.matched(k)) {
1449
+ return ""
1450
+ }
1451
+ return this.input.slice(this.slots[2 * k], this.slots[2 * k + 1])
1452
+ }
1453
+
1454
+ /** The text of the group named `name`; "" when there is none or it took no part. */
1455
+ named(name: string): string {
1456
+ return this.group(toI32(this.names.indexOf(name)))
1457
+ }
1458
+ }
1459
+
1460
+ /**
1461
+ * A compiled pattern: its program, its classes, what the prefilters read and
1462
+ * the VM's scratch memory. Made by `regexCompile`.
1463
+ */
1464
+ export class Regex {
1465
+ // Widest first, so the alignment rules insert no padding.
1466
+ /** The pattern as written. */
1467
+ readonly source: string
1468
+ /** The flags as written. */
1469
+ readonly flags: string
1470
+ /** Each repeat node's mark slot, or -1 before it has one. */
1471
+ markOf: i32[]
1472
+ names: string[]
1473
+
1474
+ op: i32[]
1475
+ ia: i32[]
1476
+ ib: i32[]
1477
+
1478
+ clsAscii: u8[]
1479
+ clsLo: i32[]
1480
+ clsHi: i32[]
1481
+ clsFrom: i32[]
1482
+ clsTo: i32[]
1483
+ clsNeg: u8[]
1484
+
1485
+ /** The literal every match starts with, or "". */
1486
+ prefix: string
1487
+ /** The prefix's rarest byte, which `findPrefix` searches for, at `rareAt`. */
1488
+ rareStr: string
1489
+ /** A literal inside every match that the part before it cannot read, or "": see `dfaInner`. */
1490
+ innerLit: string
1491
+ /** Which ASCII bytes a match can start with, or empty when any byte can start one. */
1492
+ firstSet: u8[]
1493
+ /**
1494
+ * The byte sets of a match's first `scanDepth` positions, 32 bytes each in
1495
+ * `regexScanFrom`'s layout. Every position but the last is ASCII only, so
1496
+ * each is one byte of the text.
1497
+ */
1498
+ posMasks: u8[]
1499
+
1500
+ // Two lists of threads, each with a capture table indexed by pc. A list
1501
+ // holds only the threads that wait for a character or have matched, in
1502
+ // priority order; the instructions a closure has passed through are in
1503
+ // `visit`, stamped with the closure's generation `gen`.
1504
+ cDense: i32[]
1505
+ cSlots: i32[]
1506
+ nDense: i32[]
1507
+ nSlots: i32[]
1508
+ visit: i32[]
1509
+ /** The closure's stack: explore `stkA` (kind 0) or restore slot `stkA` to `stkB` (kind 1). */
1510
+ stkKind: i32[]
1511
+ stkA: i32[]
1512
+ stkB: i32[]
1513
+ /** The captures of the thread being followed. */
1514
+ caps: i32[]
1515
+ /** The best match so far. */
1516
+ mslots: i32[]
1517
+
1518
+ // The lazy DFA, built on first use by `dfaInit`. State `id` lives at
1519
+ // offset `id << dShift` of `dTrans`, one transition per byte class; its
1520
+ // instructions are `dPool[dStart[id] .. + dLen[id])` and `dHash` finds a
1521
+ // state by them, holding `id + 1`.
1522
+ dTrans: i32[]
1523
+ dPool: i32[]
1524
+ dStart: i32[]
1525
+ dLen: i32[]
1526
+ dFlags: i32[]
1527
+ dHash: i32[]
1528
+ // Scratch for one step: the closure's stamps, leaves and stack, and the
1529
+ // next state's instructions.
1530
+ dVisit: i32[]
1531
+ dLeaf: i32[]
1532
+ dBuf: i32[]
1533
+ dStk: i32[]
1534
+ /** The class of each ASCII byte; every byte above it maps to the spare column, never filled. */
1535
+ dCls: i32[]
1536
+ /** The classes of the code points from `dWlo[i]` up to the next: every one above ASCII. */
1537
+ dWlo: i32[]
1538
+ dWcls: i32[]
1539
+ /** A code point of each class, or -1 for the end of the text, and its kind. */
1540
+ dRep: i32[]
1541
+ dNextK: i32[]
1542
+ /** The context a class leaves behind, read forward and in reverse. */
1543
+ dCtxF: i32[]
1544
+ dCtxR: i32[]
1545
+ dMaskF: i32[]
1546
+ dMaskR: i32[]
1547
+ /** Each start state's offset + 1, by mode and context, or 0. */
1548
+ dStartOff: i32[]
1549
+ /** The unanchored start state after the prefix (see `dPrefFast`): its offset + 1, or 0 until it is computed. */
1550
+ dPrefOff: i32
1551
+ /** Bytes the inner search read in vain past its literal, and bytes it moved past: see `dfaInner`. */
1552
+ innerWaste: f64
1553
+ innerGain: f64
1554
+
1555
+ /** Capture groups, the whole match included. */
1556
+ groups: i32
1557
+ /** Slots a thread carries: two per group, then one mark per repeat that can iterate empty. */
1558
+ width: i32
1559
+ /**
1560
+ * How deep the marked iterations nest, up to `REGEX_MAX_LEVELS`, so a
1561
+ * closure tells `1 << levels` sets of them apart; `checkDepth` is the
1562
+ * nesting where the compiler is.
1563
+ */
1564
+ levels: i32
1565
+ checkDepth: i32
1566
+ cCount: i32
1567
+ nCount: i32
1568
+ gen: i32
1569
+ /** The forward program's length; the reverse one follows it, at `revEntry`. */
1570
+ fwdCount: i32
1571
+ revEntry: i32
1572
+ /** The reverse of the part before `innerLit`, which the inner search runs back from it. */
1573
+ innerEntry: i32
1574
+ /** Where `dfaForward` stopped reading, and where `dfaInner`'s match starts. */
1575
+ dStop: i32
1576
+ dBegin: i32
1577
+ /** Where `rareStr` is in `prefix`. */
1578
+ rareAt: i32
1579
+ scanDepth: i32
1580
+ scanWidth: i32
1581
+ scanOff: i32
1582
+ dShift: i32
1583
+ dCount: i32
1584
+ dCap: i32
1585
+ dMax: i32
1586
+ dPoolLen: i32
1587
+ dPoolMax: i32
1588
+ dGen: i32
1589
+ /** Bumped by every flush, so a step can tell its row was cleared under it. */
1590
+ dEpoch: i32
1591
+ dFlushes: i32
1592
+ /** Where the last flush happened. */
1593
+ dPos: i32
1594
+ /** The class of the end of the text, and how many intervals above ASCII there are. */
1595
+ dEot: i32
1596
+ dWide: i32
1597
+
1598
+ hasNames: boolean
1599
+ tooBig: boolean
1600
+ /** The pattern is `prefix` and nothing else. */
1601
+ literal: boolean
1602
+ /** The pattern starts with `^` outside multiline mode. */
1603
+ anchored: boolean
1604
+ /** Whether a byte above ASCII can start a match, when `firstSet` is not empty. */
1605
+ firstWide: boolean
1606
+ /** The pattern can match the empty string. */
1607
+ nullable: boolean
1608
+ /** The reverse program was compiled. */
1609
+ revOk: boolean
1610
+ /** `dfaInit` has run, and whether the DFA may be used. */
1611
+ dReady: boolean
1612
+ dOk: boolean
1613
+ /** An idle state skips ahead with the prefilter. */
1614
+ dSkip: boolean
1615
+ /** The scan's window is rare enough in text to search with. */
1616
+ scanSel: boolean
1617
+ /** The scan's first positions are exactly the prefix's bytes, all ASCII: see `prefixIsScan`. */
1618
+ prefixExact: boolean
1619
+ /**
1620
+ * Whether no forward assertion reads the character before it, so every
1621
+ * forward state after a character holds the same context and a state with
1622
+ * no thread left is the unanchored start state wherever the text resumes.
1623
+ */
1624
+ dCtxFree: boolean
1625
+ /**
1626
+ * Whether a match can start only where the ASCII `prefix` is and no
1627
+ * assertion reads the context, so the unanchored start state after the
1628
+ * prefix's bytes is one state, at `dPrefOff`.
1629
+ */
1630
+ dPrefFast: boolean
1631
+
1632
+ constructor(source: string, flags: string) {
1633
+ this.source = source
1634
+ this.flags = flags
1635
+ this.groups = 1
1636
+ this.width = 2
1637
+ this.levels = 0
1638
+ this.checkDepth = 0
1639
+ this.markOf = []
1640
+ this.names = []
1641
+ this.hasNames = false
1642
+ this.op = []
1643
+ this.ia = []
1644
+ this.ib = []
1645
+ this.tooBig = false
1646
+ this.clsAscii = []
1647
+ this.clsLo = []
1648
+ this.clsHi = []
1649
+ this.clsFrom = []
1650
+ this.clsTo = []
1651
+ this.clsNeg = []
1652
+ this.prefix = ""
1653
+ this.literal = false
1654
+ this.anchored = false
1655
+ this.innerLit = ""
1656
+ this.firstSet = []
1657
+ this.posMasks = []
1658
+ this.firstWide = false
1659
+ this.cDense = []
1660
+ this.cSlots = []
1661
+ this.cCount = 0
1662
+ this.nDense = []
1663
+ this.nSlots = []
1664
+ this.nCount = 0
1665
+ this.visit = []
1666
+ this.gen = 0
1667
+ this.stkKind = []
1668
+ this.stkA = []
1669
+ this.stkB = []
1670
+ this.caps = []
1671
+ this.mslots = []
1672
+ this.rareStr = ""
1673
+ this.rareAt = 0
1674
+ this.scanDepth = 0
1675
+ this.scanWidth = 0
1676
+ this.scanOff = 0
1677
+ this.fwdCount = 0
1678
+ this.revEntry = 0
1679
+ this.innerLit = ""
1680
+ this.innerEntry = 0
1681
+ this.innerWaste = 0.0
1682
+ this.innerGain = 0.0
1683
+ this.dStop = 0
1684
+ this.dBegin = 0
1685
+ this.nullable = true
1686
+ this.revOk = false
1687
+ this.dTrans = []
1688
+ this.dPool = []
1689
+ this.dStart = []
1690
+ this.dLen = []
1691
+ this.dFlags = []
1692
+ this.dHash = []
1693
+ this.dVisit = []
1694
+ this.dLeaf = []
1695
+ this.dBuf = []
1696
+ this.dStk = []
1697
+ this.dCls = []
1698
+ this.dWlo = []
1699
+ this.dWcls = []
1700
+ this.dRep = []
1701
+ this.dNextK = []
1702
+ this.dCtxF = []
1703
+ this.dCtxR = []
1704
+ this.dMaskF = []
1705
+ this.dMaskR = []
1706
+ this.dCtxFree = false
1707
+ this.dStartOff = []
1708
+ this.dPrefFast = false
1709
+ this.dPrefOff = 0
1710
+ this.dShift = 0
1711
+ this.dCount = 0
1712
+ this.dCap = 0
1713
+ this.dMax = 0
1714
+ this.dPoolLen = 0
1715
+ this.dPoolMax = 0
1716
+ this.dGen = 0
1717
+ this.dEpoch = 0
1718
+ this.dFlushes = 0
1719
+ this.dPos = 0
1720
+ this.dEot = 0
1721
+ this.dWide = 0
1722
+ this.dReady = false
1723
+ this.dOk = false
1724
+ this.dSkip = false
1725
+ this.scanSel = false
1726
+ this.prefixExact = false
1727
+ }
1728
+
1729
+ /** How many groups there are, the whole match (group 0) included. */
1730
+ groupCount(): i32 {
1731
+ return this.groups
1732
+ }
1733
+
1734
+ /** The number of the group named `name`, or -1. */
1735
+ groupIndex(name: string): i32 {
1736
+ if (name === "") {
1737
+ return -1
1738
+ }
1739
+ return toI32(this.names.indexOf(name))
1740
+ }
1741
+
1742
+ emit(op: i32, a: i32, b: i32): i32 {
1743
+ const pc: i32 = toI32(this.op.length)
1744
+ if (pc >= REGEX_MAX_INSTRUCTIONS) {
1745
+ this.tooBig = true
1746
+ return 0
1747
+ }
1748
+ this.op.push(op)
1749
+ this.ia.push(a)
1750
+ this.ib.push(b)
1751
+ return pc
1752
+ }
1753
+
1754
+ pc(): i32 {
1755
+ return toI32(this.op.length)
1756
+ }
1757
+
1758
+ /** One iteration of a repeat's body: its groups cleared, then the body. */
1759
+ compileIteration(p: RegexParser, r: i32, rev: boolean): void {
1760
+ if (p.ne[r] > p.nd[r]) {
1761
+ this.emit(OP_CLEAR, 2 * p.nd[r], 2 * p.ne[r])
1762
+ }
1763
+ this.compileNode(p, p.kid[r], rev)
1764
+ }
1765
+
1766
+ /**
1767
+ * Emit node `n`. Reversed (`rev`), it matches the same text read from its
1768
+ * end: a concatenation's parts come last first and each assertion trades
1769
+ * places with its mirror, `^` with `$`. The reverse DFA runs that program.
1770
+ */
1771
+ compileNode(p: RegexParser, n: i32, rev: boolean): void {
1772
+ if (this.tooBig) {
1773
+ return
1774
+ }
1775
+ const kind: i32 = p.kind[n]
1776
+ if (kind === NODE_EMPTY) {
1777
+ return
1778
+ }
1779
+ if (kind === NODE_CHAR) {
1780
+ this.emit(OP_CHAR, p.na[n], p.nb[n])
1781
+ return
1782
+ }
1783
+ if (kind === NODE_CLASS) {
1784
+ this.emit(OP_CLASS, p.na[n], REGEX_NIL)
1785
+ return
1786
+ }
1787
+ if (kind === NODE_ANY) {
1788
+ this.emit(p.dotAll ? OP_ANY : OP_ANY_NL, REGEX_NIL, REGEX_NIL)
1789
+ return
1790
+ }
1791
+ if (kind === NODE_ASSERT) {
1792
+ // The text and line assertions are numbered so that the low bit picks
1793
+ // the beginning or the end.
1794
+ const a: i32 = p.na[n]
1795
+ this.emit(OP_ASSERT, rev && a < ASSERT_WORD ? a ^ 1 : a, REGEX_NIL)
1796
+ return
1797
+ }
1798
+ if (kind === NODE_CAT) {
1799
+ const kids: i32[] = []
1800
+ let c: i32 = p.kid[n]
1801
+ while (c !== -1) {
1802
+ kids.push(c)
1803
+ c = p.next[c]
1804
+ }
1805
+ const count: i32 = toI32(kids.length)
1806
+ let i: i32 = 0
1807
+ while (i < count) {
1808
+ let k: i32 = i
1809
+ if (rev) {
1810
+ k = count - 1 - i
1811
+ }
1812
+ // Always true: written because it is what proves `k` within `kids`.
1813
+ if (k >= 0 && k < toI32(kids.length)) {
1814
+ this.compileNode(p, kids[k], rev)
1815
+ }
1816
+ i += 1
1817
+ }
1818
+ return
1819
+ }
1820
+ if (kind === NODE_GROUP) {
1821
+ this.emit(OP_SAVE, 2 * p.na[n], REGEX_NIL)
1822
+ this.compileNode(p, p.kid[n], rev)
1823
+ this.emit(OP_SAVE, 2 * p.na[n] + 1, REGEX_NIL)
1824
+ return
1825
+ }
1826
+ if (kind === NODE_ALT) {
1827
+ const jumps: i32[] = []
1828
+ let c: i32 = p.kid[n]
1829
+ while (c !== -1 && !this.tooBig) {
1830
+ if (p.next[c] !== -1) {
1831
+ const split: i32 = this.emit(OP_SPLIT, REGEX_NIL, REGEX_NIL)
1832
+ this.ia[split] = this.pc()
1833
+ this.compileNode(p, c, rev)
1834
+ jumps.push(this.emit(OP_JMP, REGEX_NIL, REGEX_NIL))
1835
+ this.ib[split] = this.pc()
1836
+ } else {
1837
+ this.compileNode(p, c, rev)
1838
+ }
1839
+ c = p.next[c]
1840
+ }
1841
+ const exit: i32 = this.pc()
1842
+ for (const j of jumps) {
1843
+ this.ia[j] = exit
1844
+ }
1845
+ return
1846
+ }
1847
+ // NODE_REPEAT
1848
+ const min: i32 = p.na[n]
1849
+ const max: i32 = p.nb[n]
1850
+ const greedy: boolean = p.nc[n] === 1
1851
+ if (max !== min && regexNullable(p, p.kid[n])) {
1852
+ this.compileEmptyCheckedRepeat(p, n, min, max, greedy, rev)
1853
+ return
1854
+ }
1855
+ if (max === -1 && min === 0) {
1856
+ // L: SPLIT body, exit; body; JMP L
1857
+ const split: i32 = this.emit(OP_SPLIT, REGEX_NIL, REGEX_NIL)
1858
+ this.compileIteration(p, n, rev)
1859
+ this.emit(OP_JMP, split, REGEX_NIL)
1860
+ const exit: i32 = this.pc()
1861
+ this.ia[split] = greedy ? split + 1 : exit
1862
+ this.ib[split] = greedy ? exit : split + 1
1863
+ return
1864
+ }
1865
+ if (max === -1) {
1866
+ // body × (min - 1); L: body; SPLIT L, exit
1867
+ let i: i32 = 1
1868
+ while (i < min && !this.tooBig) {
1869
+ this.compileIteration(p, n, rev)
1870
+ i += 1
1871
+ }
1872
+ const loop: i32 = this.pc()
1873
+ this.compileIteration(p, n, rev)
1874
+ const split: i32 = this.emit(OP_SPLIT, REGEX_NIL, REGEX_NIL)
1875
+ this.ia[split] = greedy ? loop : split + 1
1876
+ this.ib[split] = greedy ? split + 1 : loop
1877
+ return
1878
+ }
1879
+ // body × min; then (SPLIT next, exit; body) × (max - min)
1880
+ let i: i32 = 0
1881
+ while (i < min && !this.tooBig) {
1882
+ // A body that emits nothing emits nothing on every copy: stop at one,
1883
+ // or (?:){1000} nested four deep compiles 1000^4 empty copies.
1884
+ const before: i32 = this.pc()
1885
+ this.compileIteration(p, n, rev)
1886
+ i += 1
1887
+ if (this.pc() === before) {
1888
+ break
1889
+ }
1890
+ }
1891
+ const splits: i32[] = []
1892
+ i = min
1893
+ while (i < max && !this.tooBig) {
1894
+ splits.push(this.emit(OP_SPLIT, REGEX_NIL, REGEX_NIL))
1895
+ this.compileIteration(p, n, rev)
1896
+ i += 1
1897
+ }
1898
+ const exit: i32 = this.pc()
1899
+ for (const s of splits) {
1900
+ this.ia[s] = greedy ? s + 1 : exit
1901
+ this.ib[s] = greedy ? exit : s + 1
1902
+ }
1903
+ }
1904
+
1905
+ /**
1906
+ * A repeat whose body can match empty, which JavaScript iterates by its own
1907
+ * rule: an iteration past the minimum that consumes nothing fails, and the
1908
+ * search goes on inside that iteration, at its next alternative. A Pike VM
1909
+ * left to itself would instead let the empty iteration end the loop, so
1910
+ * `(|a)+` on "a" would match "" where JavaScript matches "a". So the
1911
+ * mandatory iterations are copies of their own and each optional one is
1912
+ * bracketed by a mark of where it started and a check that it moved on.
1913
+ *
1914
+ * body × min; L: SPLIT M, exit; M: MARK k; body; CHECK k; JMP L (unbounded)
1915
+ * body × min; (SPLIT M, exit; M: MARK k; body; CHECK k) × (max - min)
1916
+ */
1917
+ compileEmptyCheckedRepeat(p: RegexParser, n: i32, min: i32, max: i32, greedy: boolean, rev: boolean): void {
1918
+ while (toI32(this.markOf.length) <= n) {
1919
+ this.markOf.push(REGEX_NONE)
1920
+ }
1921
+ if (this.markOf[n] < 0) {
1922
+ this.markOf[n] = this.width
1923
+ this.width += 1
1924
+ }
1925
+ const k: i32 = this.markOf[n]
1926
+ let i: i32 = 0
1927
+ while (i < min && !this.tooBig) {
1928
+ // A body that emits nothing emits nothing on every copy: stop at one,
1929
+ // or (?:){1000} nested four deep compiles 1000^4 empty copies.
1930
+ const before: i32 = this.pc()
1931
+ this.compileIteration(p, n, rev)
1932
+ i += 1
1933
+ if (this.pc() === before) {
1934
+ break
1935
+ }
1936
+ }
1937
+ const splits: i32[] = []
1938
+ if (max === -1) {
1939
+ const split: i32 = this.emit(OP_SPLIT, REGEX_NIL, REGEX_NIL)
1940
+ splits.push(split)
1941
+ this.compileMarked(p, n, k, rev)
1942
+ this.emit(OP_JMP, split, REGEX_NIL)
1943
+ } else {
1944
+ while (i < max && !this.tooBig) {
1945
+ splits.push(this.emit(OP_SPLIT, REGEX_NIL, REGEX_NIL))
1946
+ this.compileMarked(p, n, k, rev)
1947
+ i += 1
1948
+ }
1949
+ }
1950
+ const exit: i32 = this.pc()
1951
+ for (const s of splits) {
1952
+ this.ia[s] = greedy ? s + 1 : exit
1953
+ this.ib[s] = greedy ? exit : s + 1
1954
+ }
1955
+ }
1956
+
1957
+ /**
1958
+ * One optional iteration of repeat `n`, between a mark in slot `k` and the
1959
+ * check that it moved on. The mark records how deep it nests, which the VM
1960
+ * keys its closure's stamps by.
1961
+ */
1962
+ compileMarked(p: RegexParser, n: i32, k: i32, rev: boolean): void {
1963
+ const level: i32 = this.checkDepth
1964
+ if (level < REGEX_MAX_LEVELS && level >= this.levels) {
1965
+ this.levels = level + 1
1966
+ }
1967
+ this.emit(OP_MARK, k, level)
1968
+ this.checkDepth += 1
1969
+ this.compileIteration(p, n, rev)
1970
+ this.checkDepth -= 1
1971
+ this.emit(OP_CHECK, k, REGEX_NIL)
1972
+ }
1973
+
1974
+ /** Compile the parsed pattern and size the VM. Answers an error, or "". */
1975
+ build(p: RegexParser, root: i32): string {
1976
+ this.groups = p.groups
1977
+ this.width = 2 * p.groups
1978
+ this.names = p.names
1979
+ this.hasNames = p.hasNames
1980
+ this.clsAscii = p.clsAscii
1981
+ this.clsLo = p.clsLo
1982
+ this.clsHi = p.clsHi
1983
+ this.clsFrom = p.clsFrom
1984
+ this.clsTo = p.clsTo
1985
+ this.clsNeg = p.clsNeg
1986
+ this.emit(OP_SAVE, 2 * REGEX_WHOLE, REGEX_NIL)
1987
+ this.compileNode(p, root, false)
1988
+ this.emit(OP_SAVE, 2 * REGEX_WHOLE + 1, REGEX_NIL)
1989
+ this.emit(OP_MATCH, REGEX_NIL, REGEX_NIL)
1990
+ if (this.tooBig) {
1991
+ return "the pattern compiles to more than 100000 instructions"
1992
+ }
1993
+ const count: i32 = this.pc()
1994
+ this.fwdCount = count
1995
+ this.nullable = regexNullable(p, root)
1996
+ // The reverse program, which the reverse DFA runs to find where a match
1997
+ // starts once the forward one has found where it ends. Without marks
1998
+ // only: a mark reorders the matches, and the reverse search reads none.
1999
+ if (this.width === 2 * this.groups && 2 * count <= REGEX_MAX_INSTRUCTIONS) {
2000
+ this.revEntry = count
2001
+ this.compileNode(p, root, true)
2002
+ this.emit(OP_MATCH, REGEX_NIL, REGEX_NIL)
2003
+ this.revOk = !this.tooBig
2004
+ this.tooBig = false
2005
+ }
2006
+ const slots: i32 = this.width
2007
+ if (count > REGEX_MAX_SLOT_TABLE / slots) {
2008
+ return "too many groups for the pattern's size"
2009
+ }
2010
+ this.cDense = new Array<i32>(count)
2011
+ this.cSlots = new Array<i32>(count * slots)
2012
+ this.nDense = new Array<i32>(count)
2013
+ this.nSlots = new Array<i32>(count * slots)
2014
+ const states: i32 = 1 << this.levels
2015
+ this.visit = new Array<i32>(count * states)
2016
+ this.caps = new Array<i32>(slots)
2017
+ this.mslots = new Array<i32>(slots)
2018
+ // Each instruction is visited once per closure and set of marks, and
2019
+ // pushes at most one frame, or one per slot it clears.
2020
+ let frames: i32 = 1
2021
+ let pc: i32 = 0
2022
+ while (pc < count) {
2023
+ const o: i32 = this.op[pc]
2024
+ if (o === OP_SPLIT || o === OP_SAVE || o === OP_MARK) {
2025
+ frames += 1
2026
+ } else if (o === OP_CLEAR) {
2027
+ frames += this.ib[pc] - this.ia[pc]
2028
+ }
2029
+ pc += 1
2030
+ }
2031
+ if (frames > REGEX_MAX_SLOT_TABLE / states) {
2032
+ return "too many groups for the pattern's size"
2033
+ }
2034
+ frames *= states
2035
+ this.stkKind = new Array<i32>(frames)
2036
+ this.stkA = new Array<i32>(frames)
2037
+ this.stkB = new Array<i32>(frames)
2038
+ this.findPrefilters()
2039
+ if (this.revOk && !this.anchored && this.prefix === "") {
2040
+ this.findInner(p, root)
2041
+ }
2042
+ return ""
2043
+ }
2044
+
2045
+ /**
2046
+ * A run of literal ASCII characters among the top-level parts of the
2047
+ * pattern, not the first, whose first byte no part before it can read:
2048
+ * every match then holds the run's first occurrence at or after where it
2049
+ * starts, so the search looks for the run and runs back from it. Takes the
2050
+ * run with the rarest first byte, the longest of those, and compiles the
2051
+ * parts before it reversed, at `innerEntry`.
2052
+ */
2053
+ findInner(p: RegexParser, root: i32): void {
2054
+ if (p.kind[root] !== NODE_CAT) {
2055
+ return
2056
+ }
2057
+ const kids: i32[] = []
2058
+ let c: i32 = p.kid[root]
2059
+ while (c !== -1) {
2060
+ kids.push(c)
2061
+ c = p.next[c]
2062
+ }
2063
+ const count: i32 = toI32(kids.length)
2064
+ let best: i32 = 0
2065
+ let bestLen: i32 = 0
2066
+ let bestScore: i32 = 0
2067
+ let i: i32 = 1
2068
+ while (i >= 0 && i < toI32(kids.length)) {
2069
+ // Read before the calls below, which end what the loop's test proves.
2070
+ const first: i32 = kids[i]
2071
+ let n: i32 = 0
2072
+ while (i + n < count && this.isAsciiChar(p, kids[i + n])) {
2073
+ n += 1
2074
+ }
2075
+ if (n > 0) {
2076
+ const b: i32 = p.na[first]
2077
+ const score: i32 = regexByteScore(b)
2078
+ if (bestLen === 0 || score < bestScore || (score === bestScore && n > bestLen)) {
2079
+ let j: i32 = 0
2080
+ while (j < i && !this.canConsume(p, kids[j], b)) {
2081
+ j += 1
2082
+ }
2083
+ if (j === i) {
2084
+ best = i
2085
+ bestLen = n
2086
+ bestScore = score
2087
+ }
2088
+ }
2089
+ i += n
2090
+ } else {
2091
+ i += 1
2092
+ }
2093
+ }
2094
+ if (bestLen === 0) {
2095
+ return
2096
+ }
2097
+ const bytes: u8[] = new Array<u8>(bestLen)
2098
+ i = 0
2099
+ while (i < toI32(bytes.length)) {
2100
+ bytes[i] = toU8(p.na[kids[best + i]])
2101
+ i += 1
2102
+ }
2103
+ this.innerEntry = this.pc()
2104
+ i = best - 1
2105
+ while (i >= 0 && i < toI32(kids.length)) {
2106
+ this.compileNode(p, kids[i], true)
2107
+ i -= 1
2108
+ }
2109
+ this.emit(OP_MATCH, REGEX_NIL, REGEX_NIL)
2110
+ if (this.tooBig) {
2111
+ this.tooBig = false
2112
+ return
2113
+ }
2114
+ this.innerLit = String.fromBytes(bytes, 0, bestLen)
2115
+ }
2116
+
2117
+ /** Whether node `n` is one ASCII character, matched exactly. */
2118
+ isAsciiChar(p: RegexParser, n: i32): boolean {
2119
+ return p.kind[n] === NODE_CHAR && p.na[n] === p.nb[n] && p.na[n] < 128
2120
+ }
2121
+
2122
+ /** Whether node `n` can read the ASCII byte `b`. */
2123
+ canConsume(p: RegexParser, n: i32, b: i32): boolean {
2124
+ const kind: i32 = p.kind[n]
2125
+ if (kind === NODE_CHAR) {
2126
+ return p.na[n] === b || p.nb[n] === b
2127
+ }
2128
+ if (kind === NODE_CLASS) {
2129
+ return this.classHas(p.na[n], b)
2130
+ }
2131
+ if (kind === NODE_ANY) {
2132
+ return true
2133
+ }
2134
+ if (kind === NODE_GROUP || kind === NODE_REPEAT) {
2135
+ return this.canConsume(p, p.kid[n], b)
2136
+ }
2137
+ if (kind === NODE_CAT || kind === NODE_ALT) {
2138
+ let c: i32 = p.kid[n]
2139
+ while (c !== -1) {
2140
+ if (this.canConsume(p, c, b)) {
2141
+ return true
2142
+ }
2143
+ c = p.next[c]
2144
+ }
2145
+ }
2146
+ return false
2147
+ }
2148
+
2149
+ /** The literal prefix, whether the pattern is only that literal, the anchor, and the first-byte set. */
2150
+ findPrefilters(): void {
2151
+ const bytes: u8[] = []
2152
+ let pc: i32 = 0
2153
+ while (true) {
2154
+ const o: i32 = this.op[pc]
2155
+ const a: i32 = this.ia[pc]
2156
+ if (o === OP_SAVE) {
2157
+ pc += 1
2158
+ continue
2159
+ }
2160
+ if (o === OP_CHAR && a === this.ib[pc] && !(a >= 0xd800 && a <= 0xdfff)) {
2161
+ regexPushUtf8(bytes, a)
2162
+ pc += 1
2163
+ continue
2164
+ }
2165
+ break
2166
+ }
2167
+ const n: i32 = toI32(bytes.length)
2168
+ if (n > 0) {
2169
+ this.prefix = String.fromBytes(bytes, 0, n)
2170
+ this.literal = this.op[pc] === OP_MATCH && this.groups === 1
2171
+ let best: i32 = 1000
2172
+ let i: i32 = 0
2173
+ while (i < toI32(bytes.length)) {
2174
+ const b: i32 = toI32(bytes[i])
2175
+ if (b < 128 && regexByteScore(b) < best) {
2176
+ best = regexByteScore(b)
2177
+ this.rareAt = i
2178
+ }
2179
+ i += 1
2180
+ }
2181
+ this.rareStr = this.prefix.slice(this.rareAt, this.rareAt + 1)
2182
+ }
2183
+ pc = 0
2184
+ while (this.op[pc] === OP_SAVE) {
2185
+ pc += 1
2186
+ }
2187
+ this.anchored = this.op[pc] === OP_ASSERT && this.ia[pc] === ASSERT_BEGIN_TEXT
2188
+ if (!this.anchored) {
2189
+ this.findScan()
2190
+ }
2191
+ }
2192
+
2193
+ /**
2194
+ * The byte sets of a match's first positions, and the window of them the
2195
+ * scan searches for. Each position's set is every byte the program can read
2196
+ * there, from the instructions a match can have reached after that many
2197
+ * bytes, which is more than a match can always read there but never less.
2198
+ * The walk stops at a position where a match can already end, which makes
2199
+ * the positions before it ones every match has, and after a position where
2200
+ * a character above ASCII can start, since the next byte of the text may be
2201
+ * inside that character.
2202
+ */
2203
+ findScan(): void {
2204
+ const count: i32 = this.pc()
2205
+ const masks: u8[] = new Array<u8>(32 * REGEX_SCAN_DEPTH)
2206
+ const odds: i32[] = new Array<i32>(REGEX_SCAN_DEPTH)
2207
+ // `seen[at]` is the depth plus one at which `at` was last walked.
2208
+ const seen: i32[] = new Array<i32>(count)
2209
+ let frontier: i32[] = [REGEX_ENTRY]
2210
+ let depth: i32 = 0
2211
+ let ended: boolean = false
2212
+ while (depth < REGEX_SCAN_DEPTH && !ended) {
2213
+ const set: u8[] = new Array<u8>(128)
2214
+ const next: i32[] = []
2215
+ let wide: boolean = false
2216
+ const work: i32[] = []
2217
+ let f: i32 = 0
2218
+ while (f < toI32(frontier.length)) {
2219
+ work.push(frontier[f])
2220
+ f += 1
2221
+ }
2222
+ while (toI32(work.length) > 0) {
2223
+ // The top of the stack, read before it is popped: `pop` answers
2224
+ // `T | undefined` to TypeScript. The test always holds inside the loop
2225
+ // and is what proves the index.
2226
+ const top: i32 = toI32(work.length) - 1
2227
+ if (top < 0 || top >= toI32(work.length)) {
2228
+ break
2229
+ }
2230
+ const at: i32 = work[top]
2231
+ work.pop()
2232
+ if (at < 0 || at >= toI32(seen.length) || seen[at] === depth + 1) {
2233
+ continue
2234
+ }
2235
+ seen[at] = depth + 1
2236
+ const o: i32 = this.op[at]
2237
+ if (o === OP_JMP) {
2238
+ work.push(this.ia[at])
2239
+ } else if (o === OP_SPLIT) {
2240
+ work.push(this.ib[at])
2241
+ work.push(this.ia[at])
2242
+ } else if (o === OP_SAVE || o === OP_CLEAR || o === OP_ASSERT || o === OP_MARK || o === OP_CHECK) {
2243
+ work.push(at + 1)
2244
+ } else if (o === OP_CHAR) {
2245
+ const lower: i32 = this.ia[at]
2246
+ const other: i32 = this.ib[at]
2247
+ if (lower >= 0 && lower < toI32(set.length)) {
2248
+ set[lower] = 1
2249
+ } else {
2250
+ wide = true
2251
+ }
2252
+ if (other >= 0 && other < toI32(set.length)) {
2253
+ set[other] = 1
2254
+ } else {
2255
+ wide = true
2256
+ }
2257
+ next.push(at + 1)
2258
+ } else if (o === OP_CLASS) {
2259
+ const k: i32 = this.ia[at]
2260
+ if (this.clsNeg[k] === 1 || this.clsTo[k] > this.clsFrom[k]) {
2261
+ wide = true
2262
+ }
2263
+ let c: i32 = 0
2264
+ while (c >= 0 && c < toI32(set.length)) {
2265
+ if (this.clsAscii[k * 128 + c] === 1) {
2266
+ set[c] = 1
2267
+ }
2268
+ c += 1
2269
+ }
2270
+ next.push(at + 1)
2271
+ } else if (o === OP_ANY || o === OP_ANY_NL) {
2272
+ let c: i32 = 0
2273
+ while (c >= 0 && c < toI32(set.length)) {
2274
+ set[c] = 1
2275
+ c += 1
2276
+ }
2277
+ wide = true
2278
+ } else {
2279
+ // OP_MATCH: a match can end here.
2280
+ ended = true
2281
+ break
2282
+ }
2283
+ }
2284
+ if (ended) {
2285
+ break
2286
+ }
2287
+ let odd: i32 = wide ? 100 : 0
2288
+ let c: i32 = 0
2289
+ while (c >= 0 && c < toI32(set.length)) {
2290
+ const row: i32 = (depth << 5) | (c & 15)
2291
+ // The second test always holds: it is what proves `row`.
2292
+ if (set[c] === 1 && row >= 0 && row < toI32(masks.length)) {
2293
+ const bit: i32 = toI32(1) << (c >> 4)
2294
+ masks[row] |= toU8(bit)
2295
+ odd += regexBytePerMille(c)
2296
+ }
2297
+ c += 1
2298
+ }
2299
+ if (wide) {
2300
+ let k: i32 = 16
2301
+ while (k < 32) {
2302
+ const row: i32 = (depth << 5) | k
2303
+ if (row >= 0 && row < toI32(masks.length)) {
2304
+ masks[row] = 255
2305
+ }
2306
+ k += 1
2307
+ }
2308
+ }
2309
+ if (depth < toI32(odds.length)) {
2310
+ odds[depth] = odd > 1000 ? 1000 : odd
2311
+ }
2312
+ if (depth === 0 && this.prefix === "") {
2313
+ // The first position also feeds the VM's table, which wants a set
2314
+ // that leaves some byte out.
2315
+ let m: i32 = 0
2316
+ c = 0
2317
+ while (c >= 0 && c < toI32(set.length)) {
2318
+ m += toI32(set[c])
2319
+ c += 1
2320
+ }
2321
+ if (!wide || m < 128) {
2322
+ this.firstSet = set
2323
+ this.firstWide = wide
2324
+ }
2325
+ }
2326
+ depth += 1
2327
+ if (wide) {
2328
+ break
2329
+ }
2330
+ frontier = next
2331
+ }
2332
+ // No position, or a first one that can be inside a character: no scan.
2333
+ if (depth === 0 || masks[16] !== 0) {
2334
+ return
2335
+ }
2336
+ this.scanDepth = depth
2337
+ // The 32-byte forms the vector window reads, then a membership table of
2338
+ // 256 bytes a position, which the walker and the kernel's other positions
2339
+ // read a load a byte.
2340
+ const both: u8[] = new Array<u8>(288 * depth)
2341
+ let at: i32 = 0
2342
+ while (at >= 0 && at < 32 * depth && at < toI32(both.length) && at < toI32(masks.length)) {
2343
+ both[at] = masks[at]
2344
+ at += 1
2345
+ }
2346
+ at = 0
2347
+ while (at >= 0 && at < 256 * depth) {
2348
+ const b: i32 = at & 255
2349
+ const row: i32 = ((at >> 8) << 5) | ((b >> 7) << 4) | (b & 15)
2350
+ const slot: i32 = 32 * depth + at
2351
+ if (slot >= 0 && slot < toI32(both.length) && row >= 0 && row < toI32(masks.length)) {
2352
+ both[slot] = toU8((toI32(masks[row]) >> ((b >> 4) & 7)) & 1)
2353
+ }
2354
+ at += 1
2355
+ }
2356
+ this.posMasks = both
2357
+ // The rarest window of each width from two, and the narrowest of them
2358
+ // that lets through about one start in 256: a narrower window is fewer
2359
+ // loads a block, a wider one fewer false starts.
2360
+ const most: i32 = depth < REGEX_SCAN_WIDTH ? depth : REGEX_SCAN_WIDTH
2361
+ let width: i32 = most < 2 ? most : 2
2362
+ let best: i32 = -1
2363
+ let all: i32 = 1
2364
+ while (true) {
2365
+ all = 1
2366
+ best = -1
2367
+ let off: i32 = 0
2368
+ while (off + width <= depth) {
2369
+ let odd: i32 = 1
2370
+ let j: i32 = 0
2371
+ while (j < width) {
2372
+ odd *= odds[off + j]
2373
+ j += 1
2374
+ }
2375
+ if (best < 0 || odd < best) {
2376
+ best = odd
2377
+ this.scanOff = off
2378
+ }
2379
+ off += 1
2380
+ }
2381
+ let j: i32 = 0
2382
+ while (j < width) {
2383
+ all *= 1000
2384
+ j += 1
2385
+ }
2386
+ if (width >= most || best < all / 256) {
2387
+ break
2388
+ }
2389
+ width += 1
2390
+ }
2391
+ this.scanWidth = width
2392
+ // Worth it when fewer than one start in sixteen passes the window.
2393
+ this.scanSel = best < all / 16
2394
+ this.prefixExact = this.prefixIsScan()
2395
+ }
2396
+
2397
+ /**
2398
+ * Whether `posMasks` starts with the prefix's bytes, each ASCII, and nothing
2399
+ * else. The positions after them hold for every match too, so a start the
2400
+ * scan finds is then the prefix's next occurrence that can begin a match.
2401
+ */
2402
+ prefixIsScan(): boolean {
2403
+ const n: i32 = toI32(this.prefix.length)
2404
+ if (n === 0 || n > this.scanDepth) {
2405
+ return false
2406
+ }
2407
+ let j: i32 = 0
2408
+ while (j < n) {
2409
+ const c: i32 = toI32(this.prefix.charCodeAt(j))
2410
+ if (c >= 128) {
2411
+ return false
2412
+ }
2413
+ let k: i32 = 0
2414
+ while (k < 32) {
2415
+ const bit: i32 = toI32(1) << (c >> 4)
2416
+ const want: i32 = k === (c & 15) ? bit : 0
2417
+ if (toI32(this.posMasks[32 * j + k]) !== want) {
2418
+ return false
2419
+ }
2420
+ k += 1
2421
+ }
2422
+ j += 1
2423
+ }
2424
+ return true
2425
+ }
2426
+
2427
+ /** Whether class `k` holds code point `cp`, which is at least 0. */
2428
+ classHas(k: i32, cp: i32): boolean {
2429
+ if (cp < 128) {
2430
+ return this.clsAscii[k * 128 + cp] === 1
2431
+ }
2432
+ let lo: i32 = this.clsFrom[k]
2433
+ let hi: i32 = this.clsTo[k]
2434
+ let found: boolean = false
2435
+ while (lo < hi) {
2436
+ const mid: i32 = (lo + hi) >> 1
2437
+ if (cp < this.clsLo[mid]) {
2438
+ hi = mid
2439
+ } else if (cp > this.clsHi[mid]) {
2440
+ lo = mid + 1
2441
+ } else {
2442
+ found = true
2443
+ break
2444
+ }
2445
+ }
2446
+ return found !== (this.clsNeg[k] === 1)
2447
+ }
2448
+
2449
+ /** Whether assertion `kind` holds at byte `p` of `text`. */
2450
+ assertHolds(kind: i32, text: string, p: i32, len: i32): boolean {
2451
+ if (kind === ASSERT_BEGIN_TEXT) {
2452
+ return p === 0
2453
+ }
2454
+ if (kind === ASSERT_END_TEXT) {
2455
+ return p === len
2456
+ }
2457
+ if (kind === ASSERT_BEGIN_LINE) {
2458
+ if (p === 0) {
2459
+ return true
2460
+ }
2461
+ const b: i32 = toI32(text.charCodeAt(p - 1))
2462
+ if (b === 10 || b === 13) {
2463
+ return true
2464
+ }
2465
+ // U+2028 and U+2029 end in E2 80 A8 and E2 80 A9.
2466
+ return (
2467
+ (b === 0xa8 || b === 0xa9) &&
2468
+ p >= 3 &&
2469
+ toI32(text.charCodeAt(p - 2)) === 0x80 &&
2470
+ toI32(text.charCodeAt(p - 3)) === 0xe2
2471
+ )
2472
+ }
2473
+ if (kind === ASSERT_END_LINE) {
2474
+ if (p >= len) {
2475
+ return true
2476
+ }
2477
+ const b: i32 = toI32(text.charCodeAt(p))
2478
+ if (b === 10 || b === 13) {
2479
+ return true
2480
+ }
2481
+ if (b !== 0xe2 || p + 2 >= len) {
2482
+ return false
2483
+ }
2484
+ const b2: i32 = toI32(text.charCodeAt(p + 2))
2485
+ return toI32(text.charCodeAt(p + 1)) === 0x80 && (b2 === 0xa8 || b2 === 0xa9)
2486
+ }
2487
+ const before: boolean = p > 0 && isRegexWordByte(toI32(text.charCodeAt(p - 1)))
2488
+ const after: boolean = p < len && isRegexWordByte(toI32(text.charCodeAt(p)))
2489
+ return kind === ASSERT_WORD ? before !== after : before === after
2490
+ }
2491
+
2492
+ /** Start a closure that has visited nothing, wrapping the stamps before they overflow. */
2493
+ newGeneration(): void {
2494
+ if (this.gen >= 2147483647) {
2495
+ const count: i32 = toI32(this.visit.length)
2496
+ let pc: i32 = 0
2497
+ while (pc < count) {
2498
+ this.visit[pc] = 0
2499
+ pc += 1
2500
+ }
2501
+ this.gen = 0
2502
+ }
2503
+ this.gen += 1
2504
+ }
2505
+
2506
+ /**
2507
+ * Add the thread at `pc0` to a list, following every instruction that
2508
+ * consumes nothing, at byte `p`. The thread's captures are in `caps`, which
2509
+ * is as it was when this returns. Answers the list's new length.
2510
+ *
2511
+ * An instruction that consumes nothing is visited once for each set of
2512
+ * levels whose marks this closure has made, not once in all: a thread
2513
+ * that comes round a loop, past a check and through a new mark, is not the
2514
+ * thread that was here before it, since the check it meets next fails for
2515
+ * it alone. `(?:a*?)*` on "aa" goes on to the second `a` that way.
2516
+ */
2517
+ addThread(dense: i32[], slots: i32[], count0: i32, pc0: i32, text: string, p: i32, len: i32, ns: i32): i32 {
2518
+ let count: i32 = count0
2519
+ const states: i32 = 1 << this.levels
2520
+ this.stkKind[0] = 0
2521
+ this.stkA[0] = pc0
2522
+ this.stkB[0] = 0
2523
+ let top: i32 = 1
2524
+ while (top > 0) {
2525
+ top -= 1
2526
+ if (this.stkKind[top] === 1) {
2527
+ this.caps[this.stkA[top]] = this.stkB[top]
2528
+ continue
2529
+ }
2530
+ let pc: i32 = this.stkA[top]
2531
+ let marked: i32 = this.stkB[top]
2532
+ while (true) {
2533
+ const o: i32 = this.op[pc]
2534
+ const at: i32 = o >= OP_SPLIT && o !== OP_MATCH ? pc * states + marked : pc * states
2535
+ if (this.visit[at] === this.gen) {
2536
+ break
2537
+ }
2538
+ this.visit[at] = this.gen
2539
+ if (o === OP_JMP) {
2540
+ pc = this.ia[pc]
2541
+ continue
2542
+ }
2543
+ if (o === OP_SPLIT) {
2544
+ this.stkKind[top] = 0
2545
+ this.stkA[top] = this.ib[pc]
2546
+ this.stkB[top] = marked
2547
+ top += 1
2548
+ pc = this.ia[pc]
2549
+ continue
2550
+ }
2551
+ if (o === OP_SAVE || o === OP_MARK) {
2552
+ const k: i32 = this.ia[pc]
2553
+ if (k < ns) {
2554
+ this.stkKind[top] = 1
2555
+ this.stkA[top] = k
2556
+ this.stkB[top] = this.caps[k]
2557
+ top += 1
2558
+ this.caps[k] = p
2559
+ if (o === OP_MARK && this.ib[pc] < this.levels) {
2560
+ marked = marked | (1 << this.ib[pc])
2561
+ }
2562
+ }
2563
+ pc += 1
2564
+ continue
2565
+ }
2566
+ if (o === OP_CLEAR) {
2567
+ let k: i32 = this.ia[pc]
2568
+ const end: i32 = this.ib[pc] < ns ? this.ib[pc] : ns
2569
+ while (k < end) {
2570
+ this.stkKind[top] = 1
2571
+ this.stkA[top] = k
2572
+ this.stkB[top] = this.caps[k]
2573
+ top += 1
2574
+ this.caps[k] = -1
2575
+ k += 1
2576
+ }
2577
+ pc += 1
2578
+ continue
2579
+ }
2580
+ if (o === OP_ASSERT) {
2581
+ if (this.assertHolds(this.ia[pc], text, p, len)) {
2582
+ pc += 1
2583
+ continue
2584
+ }
2585
+ break
2586
+ }
2587
+ if (o === OP_CHECK) {
2588
+ // Untracked marks only reorder matches, never add or remove one,
2589
+ // so a search that wants no slots lets every iteration through.
2590
+ const k: i32 = this.ia[pc]
2591
+ if (k < ns && this.caps[k] === p) {
2592
+ break
2593
+ }
2594
+ pc += 1
2595
+ continue
2596
+ }
2597
+ // A thread that waits for a character, or has matched.
2598
+ if (count >= 0 && count < toI32(dense.length)) {
2599
+ dense[count] = pc
2600
+ count += 1
2601
+ }
2602
+ const base: i32 = pc * ns
2603
+ let k: i32 = 0
2604
+ while (k < ns) {
2605
+ slots[base + k] = this.caps[k]
2606
+ k += 1
2607
+ }
2608
+ break
2609
+ }
2610
+ }
2611
+ return count
2612
+ }
2613
+
2614
+ /**
2615
+ * The first match of `prefix` at or after `from`, or -1: a search for its
2616
+ * rarest byte, then a comparison of the bytes around it.
2617
+ */
2618
+ findPrefix(text: string, from: i32): i32 {
2619
+ if (this.scanSel) {
2620
+ return this.findPrefixScan(text, from)
2621
+ }
2622
+ if (this.rareAt === 0) {
2623
+ return toI32(text.indexOf(this.prefix, from))
2624
+ }
2625
+ const len: i32 = toI32(text.length)
2626
+ const n: i32 = toI32(this.prefix.length)
2627
+ let p: i32 = from + this.rareAt
2628
+ while (true) {
2629
+ const at: i32 = toI32(text.indexOf(this.rareStr, p))
2630
+ if (at < 0) {
2631
+ return -1
2632
+ }
2633
+ const s: i32 = at - this.rareAt
2634
+ if (s > len - n) {
2635
+ return -1
2636
+ }
2637
+ let k: i32 = 0
2638
+ while (k < n && text.charCodeAt(s + k) === this.prefix.charCodeAt(k)) {
2639
+ k += 1
2640
+ }
2641
+ if (k === n) {
2642
+ return s
2643
+ }
2644
+ p = at + 1
2645
+ }
2646
+ }
2647
+
2648
+ /**
2649
+ * `findPrefix` by the scan, which is rarer than the prefix's rarest byte
2650
+ * alone. When `prefixExact` the scan's positions are the prefix's bytes and
2651
+ * a start it finds is a match.
2652
+ */
2653
+ findPrefixScan(text: string, from: i32): i32 {
2654
+ const len: i32 = toI32(text.length)
2655
+ const n: i32 = toI32(this.prefix.length)
2656
+ let p: i32 = from
2657
+ while (p >= 0 && p <= len) {
2658
+ const s: i32 = this.scanTo(text, p)
2659
+ if (s < 0 || this.prefixExact) {
2660
+ return s
2661
+ }
2662
+ if (s > len - n) {
2663
+ return -1
2664
+ }
2665
+ let k: i32 = 0
2666
+ while (k < n && text.charCodeAt(s + k) === this.prefix.charCodeAt(k)) {
2667
+ k += 1
2668
+ }
2669
+ if (k >= n) {
2670
+ return s
2671
+ }
2672
+ p = s + 1
2673
+ }
2674
+ return -1
2675
+ }
2676
+
2677
+ /**
2678
+ * The first index at or after `from`, which is at least 0, whose bytes pass
2679
+ * every position of `posMasks`, or -1. No match starts anywhere it skips.
2680
+ */
2681
+ scanTo(text: string, from: i32): i32 {
2682
+ if (from > toI32(text.length)) {
2683
+ return -1
2684
+ }
2685
+ return regexScanFrom(text, this.posMasks, this.scanDepth, this.scanOff, this.scanWidth, from)
2686
+ }
2687
+
2688
+ /** Where the next match can start at or after `p`, by the prefilter, or -1. */
2689
+ dSkipTo(text: string, p: i32): i32 {
2690
+ if (this.prefix !== "") {
2691
+ return this.findPrefix(text, p)
2692
+ }
2693
+ return this.scanTo(text, p)
2694
+ }
2695
+
2696
+ /**
2697
+ * Split the characters into the classes the program cannot tell apart, and
2698
+ * size the DFA. Each ASCII byte, the end of the text and each interval of
2699
+ * code points between the program's boundaries is a unit; every literal,
2700
+ * class and kind the assertions read splits the units it holds from those
2701
+ * it does not. Leaves `dOk` false when the split or the table would be too
2702
+ * big, and the VM does all the work.
2703
+ */
2704
+ dfaInit(): void {
2705
+ this.dReady = true
2706
+ const total: i32 = this.pc()
2707
+ const count: i32 = this.fwdCount
2708
+ let bitsF: i32 = 0
2709
+ let bitsR: i32 = 0
2710
+ let anyNl: boolean = false
2711
+ let pc: i32 = 0
2712
+ while (pc < total) {
2713
+ const o: i32 = this.op[pc]
2714
+ if (o === OP_ASSERT) {
2715
+ if (pc < count) {
2716
+ bitsF |= 1 << this.ia[pc]
2717
+ } else {
2718
+ bitsR |= 1 << this.ia[pc]
2719
+ }
2720
+ } else if (o === OP_ANY_NL) {
2721
+ anyNl = true
2722
+ }
2723
+ pc += 1
2724
+ }
2725
+ const bits: i32 = bitsF | bitsR
2726
+ const lines: boolean = anyNl || (bits & ((1 << ASSERT_BEGIN_LINE) | (1 << ASSERT_END_LINE))) !== 0
2727
+ const words: boolean = (bits & ((1 << ASSERT_WORD) | (1 << ASSERT_NOT_WORD))) !== 0
2728
+ // The boundaries above ASCII: where a literal, a range or a line
2729
+ // terminator starts and where it stops. The reverse program reads the
2730
+ // same characters, so the forward one is enough.
2731
+ const classes: i32 = toI32(this.clsFrom.length)
2732
+ const seenCls: u8[] = new Array<u8>(classes)
2733
+ const points: i32[] = []
2734
+ pc = 0
2735
+ while (pc < count) {
2736
+ const o: i32 = this.op[pc]
2737
+ if (o === OP_CHAR) {
2738
+ const a: i32 = this.ia[pc]
2739
+ const b: i32 = this.ib[pc]
2740
+ if (a >= 128) {
2741
+ points.push(a)
2742
+ points.push(a + 1)
2743
+ }
2744
+ if (b >= 128 && b !== a) {
2745
+ points.push(b)
2746
+ points.push(b + 1)
2747
+ }
2748
+ } else if (o === OP_CLASS) {
2749
+ const k: i32 = this.ia[pc]
2750
+ if (k >= 0 && k < toI32(seenCls.length) && seenCls[k] === 0) {
2751
+ seenCls[k] = 1
2752
+ let r: i32 = this.clsFrom[k]
2753
+ while (r < this.clsTo[k]) {
2754
+ const hi: i32 = this.clsHi[r]
2755
+ if (hi >= 128) {
2756
+ const lo: i32 = this.clsLo[r]
2757
+ points.push(lo > 128 ? lo : 128)
2758
+ points.push(hi + 1)
2759
+ }
2760
+ r += 1
2761
+ }
2762
+ }
2763
+ }
2764
+ pc += 1
2765
+ }
2766
+ if (lines) {
2767
+ points.push(0x2028)
2768
+ points.push(0x202a)
2769
+ }
2770
+ regexSortPoints(points)
2771
+ const lows: i32[] = [128]
2772
+ let i: i32 = 0
2773
+ while (i < toI32(points.length)) {
2774
+ const v: i32 = points[i]
2775
+ if (v > lows[toI32(lows.length) - 1]) {
2776
+ lows.push(v)
2777
+ }
2778
+ i += 1
2779
+ }
2780
+ const wide: i32 = toI32(lows.length)
2781
+ const units: i32 = 129 + wide
2782
+ if (units > DFA_MAX_UNITS) {
2783
+ return
2784
+ }
2785
+ const rep: i32[] = new Array<i32>(units)
2786
+ let u: i32 = 0
2787
+ while (u < 128 && u < toI32(rep.length)) {
2788
+ rep[u] = u
2789
+ u += 1
2790
+ }
2791
+ rep[128] = -1
2792
+ i = 0
2793
+ while (i < wide && 129 + i < toI32(rep.length)) {
2794
+ rep[129 + i] = lows[i]
2795
+ i += 1
2796
+ }
2797
+ // Refine one predicate at a time: a unit's class is its old class and
2798
+ // whether the predicate holds for it.
2799
+ const cls: i32[] = new Array<i32>(units)
2800
+ const hit: u8[] = new Array<u8>(units)
2801
+ const remap: i32[] = new Array<i32>(2 * units)
2802
+ let n: i32 = 1
2803
+ let work: i32 = 0
2804
+ u = 0
2805
+ while (u < toI32(hit.length)) {
2806
+ hit[u] = toU8(u === 128 ? 1 : 0)
2807
+ u += 1
2808
+ }
2809
+ n = this.dRefine(cls, hit, units, n, remap)
2810
+ const pairs: u8[] = new Array<u8>(16384)
2811
+ i = 0
2812
+ while (i < toI32(seenCls.length)) {
2813
+ seenCls[i] = 0
2814
+ i += 1
2815
+ }
2816
+ pc = 0
2817
+ while (pc < count) {
2818
+ const o: i32 = this.op[pc]
2819
+ const a: i32 = this.ia[pc]
2820
+ const b: i32 = this.ib[pc]
2821
+ let use: boolean = false
2822
+ if (o === OP_CHAR) {
2823
+ if (a < 128 && b < 128) {
2824
+ const key: i32 = a * 128 + b
2825
+ // Always true for a character: the test is what proves `key`.
2826
+ if (key >= 0 && key < toI32(pairs.length)) {
2827
+ use = pairs[key] === 0
2828
+ pairs[key] = 1
2829
+ }
2830
+ } else {
2831
+ use = true
2832
+ }
2833
+ if (use) {
2834
+ u = 0
2835
+ while (u < toI32(hit.length) && u < toI32(rep.length)) {
2836
+ hit[u] = toU8(rep[u] === a || rep[u] === b ? 1 : 0)
2837
+ u += 1
2838
+ }
2839
+ }
2840
+ } else if (o === OP_CLASS) {
2841
+ if (a >= 0 && a < toI32(seenCls.length)) {
2842
+ use = seenCls[a] === 0
2843
+ seenCls[a] = 1
2844
+ }
2845
+ if (use) {
2846
+ u = 0
2847
+ while (u < toI32(rep.length)) {
2848
+ // Read before the call, after which only a test of its own
2849
+ // proves `u` within `hit`.
2850
+ const r: i32 = rep[u]
2851
+ const has: boolean = u !== 128 && this.classHas(a, r)
2852
+ if (u < toI32(hit.length)) {
2853
+ hit[u] = toU8(has ? 1 : 0)
2854
+ }
2855
+ u += 1
2856
+ }
2857
+ }
2858
+ }
2859
+ if (use) {
2860
+ work += units
2861
+ if (work > DFA_MAX_WORK) {
2862
+ return
2863
+ }
2864
+ n = this.dRefine(cls, hit, units, n, remap)
2865
+ }
2866
+ pc += 1
2867
+ }
2868
+ if (lines) {
2869
+ u = 0
2870
+ while (u < toI32(hit.length) && u < toI32(rep.length)) {
2871
+ const r: i32 = rep[u]
2872
+ hit[u] = toU8(r === 10 || r === 13 || r === 0x2028 || r === 0x2029 ? 1 : 0)
2873
+ u += 1
2874
+ }
2875
+ n = this.dRefine(cls, hit, units, n, remap)
2876
+ }
2877
+ if (words) {
2878
+ u = 0
2879
+ while (u < toI32(hit.length) && u < toI32(rep.length)) {
2880
+ const r: i32 = rep[u]
2881
+ const word: boolean = r >= 0 && r < 128 && isRegexWordByte(r)
2882
+ if (u < toI32(hit.length)) {
2883
+ hit[u] = toU8(word ? 1 : 0)
2884
+ }
2885
+ u += 1
2886
+ }
2887
+ n = this.dRefine(cls, hit, units, n, remap)
2888
+ }
2889
+ // One column past the classes for every byte above ASCII, whose entries
2890
+ // stay 0: the hot loop reads such a byte as a transition it cannot take
2891
+ // and leaves for the slow path, which decodes the character.
2892
+ let shift: i32 = 0
2893
+ while (1 << shift < n + 1) {
2894
+ shift += 1
2895
+ }
2896
+ if (shift > DFA_MAX_SHIFT) {
2897
+ return
2898
+ }
2899
+ this.dShift = shift
2900
+ this.dCls = new Array<i32>(256)
2901
+ u = 0
2902
+ while (u < 256) {
2903
+ this.dCls[u] = u < 128 && u < toI32(cls.length) ? cls[u] : n
2904
+ u += 1
2905
+ }
2906
+ this.dEot = cls[128]
2907
+ this.dWlo = lows
2908
+ this.dWide = wide
2909
+ this.dWcls = new Array<i32>(wide)
2910
+ i = 0
2911
+ while (i < wide && 129 + i < toI32(cls.length)) {
2912
+ this.dWcls[i] = cls[129 + i]
2913
+ i += 1
2914
+ }
2915
+ // Each class's representative and kind: the first unit in it.
2916
+ this.dMaskF = regexContextMask(bitsF)
2917
+ this.dMaskR = regexContextMask(bitsR)
2918
+ this.dCtxFree =
2919
+ this.dMaskF[CTX_START] === CTX_OTHER &&
2920
+ this.dMaskF[CTX_WORD] === CTX_OTHER &&
2921
+ this.dMaskF[CTX_LINE] === CTX_OTHER
2922
+ this.dRep = new Array<i32>(n)
2923
+ this.dNextK = new Array<i32>(n)
2924
+ this.dCtxF = new Array<i32>(n)
2925
+ this.dCtxR = new Array<i32>(n)
2926
+ const done: u8[] = new Array<u8>(n)
2927
+ u = 0
2928
+ while (u < toI32(cls.length) && u < toI32(rep.length)) {
2929
+ const c: i32 = cls[u]
2930
+ const r: i32 = rep[u]
2931
+ if (c >= 0 && c < toI32(done.length) && done[c] === 0) {
2932
+ done[c] = 1
2933
+ this.dRep[c] = r
2934
+ let kind: i32 = CTX_OTHER
2935
+ if (r < 0) {
2936
+ kind = CTX_EOT
2937
+ } else if (r === 10 || r === 13 || r === 0x2028 || r === 0x2029) {
2938
+ kind = CTX_LINE
2939
+ } else if (r < 128 && isRegexWordByte(r)) {
2940
+ kind = CTX_WORD
2941
+ }
2942
+ this.dNextK[c] = kind
2943
+ this.dCtxF[c] = this.dMaskF[kind]
2944
+ this.dCtxR[c] = this.dMaskR[kind]
2945
+ }
2946
+ u += 1
2947
+ }
2948
+ this.dVisit = new Array<i32>(total)
2949
+ this.dLeaf = new Array<i32>(total)
2950
+ this.dBuf = new Array<i32>(total)
2951
+ this.dStk = new Array<i32>(total + 1)
2952
+ this.dMax = DFA_MAX_TABLE >> shift < DFA_MAX_STATES ? DFA_MAX_TABLE >> shift : DFA_MAX_STATES
2953
+ this.dCap = this.dMax < DFA_FIRST_STATES ? this.dMax : DFA_FIRST_STATES
2954
+ this.dTrans = new Array<i32>(this.dCap << shift)
2955
+ this.dStart = new Array<i32>(this.dCap)
2956
+ this.dLen = new Array<i32>(this.dCap)
2957
+ this.dFlags = new Array<i32>(this.dCap)
2958
+ this.dHash = new Array<i32>(this.dHashSize(this.dCap))
2959
+ this.dPoolMax = 2 * total > DFA_MIN_POOL ? 2 * total : DFA_MIN_POOL
2960
+ this.dPool = new Array<i32>(DFA_FIRST_POOL)
2961
+ this.dPoolLen = 0
2962
+ // Row 0 is never a state, so a plain transition, the next row's offset,
2963
+ // is never 0, the entry not yet computed.
2964
+ this.dCount = 1
2965
+ this.dStartOff = new Array<i32>(4 * CTX_SLOTS)
2966
+ this.dSkip = !this.anchored && (this.prefix !== "" || this.scanSel)
2967
+ this.dPrefFast = this.dSkip && this.dCtxFree && this.prefix !== "" && isRegexAscii(this.prefix)
2968
+ this.dPrefOff = 0
2969
+ this.dOk = true
2970
+ }
2971
+
2972
+ /**
2973
+ * Split each of the `n` classes of `cls` by `hit`, numbering the new
2974
+ * classes in order of first appearance. Answers how many there are.
2975
+ */
2976
+ dRefine(cls: i32[], hit: u8[], units: i32, n: i32, remap: i32[]): i32 {
2977
+ let k: i32 = 0
2978
+ while (k < 2 * n && k < toI32(remap.length)) {
2979
+ remap[k] = -1
2980
+ k += 1
2981
+ }
2982
+ let next: i32 = 0
2983
+ let u: i32 = 0
2984
+ while (u < units && u < toI32(cls.length) && u < toI32(hit.length)) {
2985
+ const key: i32 = cls[u] * 2 + toI32(hit[u])
2986
+ // Always true: a class is below `n`. The test is what proves `key`.
2987
+ if (key >= 0 && key < toI32(remap.length)) {
2988
+ if (remap[key] < 0) {
2989
+ remap[key] = next
2990
+ next += 1
2991
+ }
2992
+ cls[u] = remap[key]
2993
+ }
2994
+ u += 1
2995
+ }
2996
+ return next
2997
+ }
2998
+
2999
+ /** The smallest power of two that holds `cap` states at most half full. */
3000
+ dHashSize(cap: i32): i32 {
3001
+ let size: i32 = 1
3002
+ while (size < 2 * cap) {
3003
+ size *= 2
3004
+ }
3005
+ return size
3006
+ }
3007
+
3008
+ /** The class of code point `cp`, which is above ASCII. */
3009
+ dWideClass(cp: i32): i32 {
3010
+ if (this.dWide === 1) {
3011
+ return this.dWcls[0]
3012
+ }
3013
+ let lo: i32 = 0
3014
+ let hi: i32 = this.dWide - 1
3015
+ while (lo < hi) {
3016
+ const mid: i32 = (lo + hi + 1) >> 1
3017
+ if (this.dWlo[mid] <= cp) {
3018
+ lo = mid
3019
+ } else {
3020
+ hi = mid - 1
3021
+ }
3022
+ }
3023
+ return this.dWcls[lo]
3024
+ }
3025
+
3026
+ /** Whether assertion `kind` holds between a character of kind `ctx` and one of kind `nk`. */
3027
+ dAssert(kind: i32, ctx: i32, nk: i32): boolean {
3028
+ if (kind === ASSERT_BEGIN_TEXT) {
3029
+ return ctx === CTX_START
3030
+ }
3031
+ if (kind === ASSERT_END_TEXT) {
3032
+ return nk === CTX_EOT
3033
+ }
3034
+ if (kind === ASSERT_BEGIN_LINE) {
3035
+ return ctx === CTX_START || ctx === CTX_LINE
3036
+ }
3037
+ if (kind === ASSERT_END_LINE) {
3038
+ return nk === CTX_EOT || nk === CTX_LINE
3039
+ }
3040
+ const boundary: boolean = (ctx === CTX_WORD) !== (nk === CTX_WORD)
3041
+ return kind === ASSERT_WORD ? boundary : !boundary
3042
+ }
3043
+
3044
+ /**
3045
+ * Follow every instruction from `pc0` that consumes nothing, between a
3046
+ * character of kind `ctx` and one of kind `nk`, and append the ones that
3047
+ * wait for a character or match to `dLeaf` in priority order. Answers the
3048
+ * new length.
3049
+ */
3050
+ dClose(pc0: i32, ctx: i32, nk: i32, n0: i32): i32 {
3051
+ let n: i32 = n0
3052
+ this.dStk[0] = pc0
3053
+ let top: i32 = 1
3054
+ while (top > 0) {
3055
+ top -= 1
3056
+ let pc: i32 = this.dStk[top]
3057
+ while (true) {
3058
+ if (this.dVisit[pc] === this.dGen) {
3059
+ break
3060
+ }
3061
+ this.dVisit[pc] = this.dGen
3062
+ const o: i32 = this.op[pc]
3063
+ if (o === OP_JMP) {
3064
+ pc = this.ia[pc]
3065
+ continue
3066
+ }
3067
+ if (o === OP_SPLIT) {
3068
+ this.dStk[top] = this.ib[pc]
3069
+ top += 1
3070
+ pc = this.ia[pc]
3071
+ continue
3072
+ }
3073
+ if (o === OP_SAVE || o === OP_MARK || o === OP_CLEAR || o === OP_CHECK) {
3074
+ pc += 1
3075
+ continue
3076
+ }
3077
+ if (o === OP_ASSERT) {
3078
+ if (this.dAssert(this.ia[pc], ctx, nk)) {
3079
+ pc += 1
3080
+ continue
3081
+ }
3082
+ break
3083
+ }
3084
+ this.dLeaf[n] = pc
3085
+ n += 1
3086
+ break
3087
+ }
3088
+ }
3089
+ return n
3090
+ }
3091
+
3092
+ /**
3093
+ * Compute and record the transition from the state at `off` on class `c`,
3094
+ * read at byte `p`. Answers the entry as stored, or 0 when the cache gave up.
3095
+ */
3096
+ dStep(off: i32, c: i32, p: i32): i32 {
3097
+ const id: i32 = off >> this.dShift
3098
+ const start: i32 = this.dStart[id]
3099
+ const len: i32 = this.dLen[id]
3100
+ const flags: i32 = this.dFlags[id]
3101
+ const restart: boolean = (flags & DFA_RESTART) !== 0
3102
+ const rev: boolean = (flags & DFA_REV) !== 0
3103
+ const ctx: i32 = (flags >> 1) & 3
3104
+ const nk: i32 = this.dNextK[c]
3105
+ const epoch: i32 = this.dEpoch
3106
+ if (this.dGen >= 2147483647) {
3107
+ let k: i32 = 0
3108
+ while (k < toI32(this.dVisit.length)) {
3109
+ this.dVisit[k] = 0
3110
+ k += 1
3111
+ }
3112
+ this.dGen = 0
3113
+ }
3114
+ this.dGen += 1
3115
+ let n: i32 = 0
3116
+ let i: i32 = 0
3117
+ while (i < len) {
3118
+ n = this.dClose(this.dPool[start + i], ctx, nk, n)
3119
+ i += 1
3120
+ }
3121
+ if (restart) {
3122
+ n = this.dClose(REGEX_ENTRY, ctx, nk, n)
3123
+ }
3124
+ let matched: boolean = false
3125
+ let entry: i32 = DFA_KNOWN | DFA_DEAD
3126
+ let to: i32 = 0
3127
+ if (c === this.dEot) {
3128
+ i = 0
3129
+ while (i < n) {
3130
+ if (this.op[this.dLeaf[i]] === OP_MATCH) {
3131
+ matched = true
3132
+ break
3133
+ }
3134
+ i += 1
3135
+ }
3136
+ } else {
3137
+ const r: i32 = this.dRep[c]
3138
+ let nb: i32 = 0
3139
+ i = 0
3140
+ while (i < n) {
3141
+ const pc: i32 = this.dLeaf[i]
3142
+ i += 1
3143
+ const o: i32 = this.op[pc]
3144
+ let hit: boolean = false
3145
+ if (o === OP_CHAR) {
3146
+ hit = r === this.ia[pc] || r === this.ib[pc]
3147
+ } else if (o === OP_CLASS) {
3148
+ hit = this.classHas(this.ia[pc], r)
3149
+ } else if (o === OP_ANY) {
3150
+ hit = true
3151
+ } else if (o === OP_ANY_NL) {
3152
+ hit = nk !== CTX_LINE
3153
+ } else if (o === OP_MATCH) {
3154
+ matched = true
3155
+ if (!rev) {
3156
+ // Every thread after this one has lower priority.
3157
+ break
3158
+ }
3159
+ }
3160
+ if (hit) {
3161
+ this.dBuf[nb] = pc + 1
3162
+ nb += 1
3163
+ }
3164
+ }
3165
+ const restart2: boolean = restart && !matched
3166
+ if (nb > 0 || restart2) {
3167
+ const ctx2: i32 = rev ? this.dCtxR[c] : this.dCtxF[c]
3168
+ let flags2: i32 = ctx2 << 1
3169
+ if (restart2) {
3170
+ flags2 |= DFA_RESTART
3171
+ }
3172
+ if (rev) {
3173
+ flags2 |= DFA_REV
3174
+ }
3175
+ const next: i32 = this.dIntern(nb, flags2, p)
3176
+ if (next < 0) {
3177
+ return 0
3178
+ }
3179
+ to = next
3180
+ entry = (next << 4) | DFA_KNOWN
3181
+ if (nb === 0 && this.dSkip) {
3182
+ entry |= DFA_IDLE
3183
+ }
3184
+ }
3185
+ }
3186
+ if (matched) {
3187
+ entry |= DFA_MATCH
3188
+ }
3189
+ if (to !== 0 && entry === ((to << 4) | DFA_KNOWN)) {
3190
+ entry = to
3191
+ } else {
3192
+ entry = -entry
3193
+ }
3194
+ if (epoch === this.dEpoch) {
3195
+ this.dTrans[off + c] = entry
3196
+ }
3197
+ return entry
3198
+ }
3199
+
3200
+ /** The hash of a state's flags and its `n` instructions in `src` from `at`. */
3201
+ dHashOf(src: i32[], at: i32, n: i32, flags: i32): i32 {
3202
+ let h: i32 = flags
3203
+ let i: i32 = 0
3204
+ while (i < n) {
3205
+ h = (h * 31 + src[at + i]) & DFA_HASH_MASK
3206
+ i += 1
3207
+ }
3208
+ return h
3209
+ }
3210
+
3211
+ /**
3212
+ * The offset of the state with flags `flags` and the `n` instructions in
3213
+ * `dBuf`, made if it is new. Answers -1 when the cache is full and was
3214
+ * cleared too recently, at byte `p`, to clear again.
3215
+ */
3216
+ dIntern(n: i32, flags: i32, p: i32): i32 {
3217
+ const mask: i32 = toI32(this.dHash.length) - 1
3218
+ const at: i32 = 0
3219
+ let slot: i32 = this.dHashOf(this.dBuf, at, n, flags) & mask
3220
+ while (this.dHash[slot] !== 0) {
3221
+ const id: i32 = this.dHash[slot] - 1
3222
+ if (this.dFlags[id] === flags && this.dLen[id] === n) {
3223
+ const start: i32 = this.dStart[id]
3224
+ let k: i32 = 0
3225
+ while (k < n && this.dPool[start + k] === this.dBuf[k]) {
3226
+ k += 1
3227
+ }
3228
+ if (k === n) {
3229
+ return id << this.dShift
3230
+ }
3231
+ }
3232
+ slot = (slot + 1) & mask
3233
+ }
3234
+ if (this.dCount === this.dCap) {
3235
+ if (this.dCap < this.dMax) {
3236
+ this.dGrow()
3237
+ } else if (!this.dFlush(p)) {
3238
+ return -1
3239
+ }
3240
+ return this.dIntern(n, flags, p)
3241
+ }
3242
+ if (this.dPoolLen + n > toI32(this.dPool.length)) {
3243
+ const size: i32 = toI32(this.dPool.length)
3244
+ if (size >= this.dPoolMax) {
3245
+ if (!this.dFlush(p)) {
3246
+ return -1
3247
+ }
3248
+ return this.dIntern(n, flags, p)
3249
+ }
3250
+ let grown: i32 = 2 * size
3251
+ while (grown < this.dPoolLen + n) {
3252
+ grown *= 2
3253
+ }
3254
+ if (grown > this.dPoolMax) {
3255
+ grown = this.dPoolMax
3256
+ }
3257
+ const pool: i32[] = new Array<i32>(grown)
3258
+ let k: i32 = 0
3259
+ while (k < this.dPoolLen && k < toI32(pool.length)) {
3260
+ pool[k] = this.dPool[k]
3261
+ k += 1
3262
+ }
3263
+ this.dPool = pool
3264
+ }
3265
+ const id: i32 = this.dCount
3266
+ this.dCount += 1
3267
+ this.dStart[id] = this.dPoolLen
3268
+ this.dLen[id] = n
3269
+ this.dFlags[id] = flags
3270
+ let k: i32 = 0
3271
+ while (k < n) {
3272
+ this.dPool[this.dPoolLen + k] = this.dBuf[k]
3273
+ k += 1
3274
+ }
3275
+ this.dPoolLen += n
3276
+ const off: i32 = id << this.dShift
3277
+ const row: i32 = 1 << this.dShift
3278
+ k = 0
3279
+ while (k < row) {
3280
+ this.dTrans[off + k] = 0
3281
+ k += 1
3282
+ }
3283
+ this.dHash[slot] = id + 1
3284
+ return off
3285
+ }
3286
+
3287
+ /** Double the state table, up to `dMax` states. */
3288
+ dGrow(): void {
3289
+ let cap: i32 = 2 * this.dCap
3290
+ if (cap > this.dMax) {
3291
+ cap = this.dMax
3292
+ }
3293
+ const used: i32 = this.dCount << this.dShift
3294
+ const trans: i32[] = new Array<i32>(cap << this.dShift)
3295
+ let k: i32 = 0
3296
+ while (k < used && k < toI32(trans.length)) {
3297
+ trans[k] = this.dTrans[k]
3298
+ k += 1
3299
+ }
3300
+ const start: i32[] = new Array<i32>(cap)
3301
+ const len: i32[] = new Array<i32>(cap)
3302
+ const flags: i32[] = new Array<i32>(cap)
3303
+ const hash: i32[] = new Array<i32>(this.dHashSize(cap))
3304
+ const mask: i32 = toI32(hash.length) - 1
3305
+ // Every length test below always holds (`dCount` is at most `cap`, and
3306
+ // `& mask` keeps a slot within `hash`): each is what proves its index.
3307
+ k = 1
3308
+ while (k < this.dCount && k < toI32(start.length) && k < toI32(len.length) && k < toI32(flags.length)) {
3309
+ const at: i32 = this.dStart[k]
3310
+ const size: i32 = this.dLen[k]
3311
+ const kind: i32 = this.dFlags[k]
3312
+ start[k] = at
3313
+ len[k] = size
3314
+ flags[k] = kind
3315
+ let slot: i32 = this.dHashOf(this.dPool, at, size, kind) & mask
3316
+ while (slot >= 0 && slot < toI32(hash.length) && hash[slot] !== 0) {
3317
+ slot = (slot + 1) & mask
3318
+ }
3319
+ if (slot >= 0 && slot < toI32(hash.length)) {
3320
+ hash[slot] = k + 1
3321
+ }
3322
+ k += 1
3323
+ }
3324
+ this.dTrans = trans
3325
+ this.dStart = start
3326
+ this.dLen = len
3327
+ this.dFlags = flags
3328
+ this.dHash = hash
3329
+ this.dCap = cap
3330
+ }
3331
+
3332
+ /**
3333
+ * Forget every state, unless the cache was cleared more than a few times
3334
+ * already and fewer than 10 bytes per state ago: then the DFA is building a
3335
+ * state for nearly every byte and the VM is cheaper. Answers whether it
3336
+ * cleared.
3337
+ */
3338
+ dFlush(p: i32): boolean {
3339
+ this.dFlushes += 1
3340
+ const moved: i32 = p > this.dPos ? p - this.dPos : this.dPos - p
3341
+ if (this.dFlushes > DFA_FREE_FLUSHES && moved < DFA_BYTES_PER_STATE * this.dMax) {
3342
+ return false
3343
+ }
3344
+ this.dPos = p
3345
+ this.dCount = 1
3346
+ this.dPoolLen = 0
3347
+ let k: i32 = 0
3348
+ while (k < toI32(this.dHash.length)) {
3349
+ this.dHash[k] = 0
3350
+ k += 1
3351
+ }
3352
+ k = 0
3353
+ while (k < toI32(this.dStartOff.length)) {
3354
+ this.dStartOff[k] = 0
3355
+ k += 1
3356
+ }
3357
+ this.dPrefOff = 0
3358
+ this.dEpoch = (this.dEpoch + 1) & DFA_EPOCH_MASK
3359
+ return true
3360
+ }
3361
+
3362
+ /** The start state for `mode` after a character of kind `ctx`, or -1. */
3363
+ dStartAt(mode: i32, ctx: i32, p: i32): i32 {
3364
+ const k: i32 = mode * CTX_SLOTS + ctx
3365
+ const cached: i32 = this.dStartOff[k]
3366
+ if (cached > 0) {
3367
+ return cached - 1
3368
+ }
3369
+ let n: i32 = 0
3370
+ let flags: i32 = ctx << 1
3371
+ if (mode === DFA_UNANCHORED) {
3372
+ flags |= DFA_RESTART
3373
+ } else if (mode === DFA_ANCHORED) {
3374
+ this.dBuf[0] = REGEX_ENTRY
3375
+ n = 1
3376
+ } else {
3377
+ this.dBuf[0] = mode === DFA_INNER ? this.innerEntry : this.revEntry
3378
+ n = 1
3379
+ flags |= DFA_REV
3380
+ }
3381
+ const off: i32 = this.dIntern(n, flags, p)
3382
+ if (off >= 0) {
3383
+ this.dStartOff[k] = off + 1
3384
+ }
3385
+ return off
3386
+ }
3387
+
3388
+ /**
3389
+ * The unanchored start state after the bytes of `prefix`, read from byte
3390
+ * `p`, or -1 when a step on the way is not a plain transition; then
3391
+ * `dPrefFast` goes off.
3392
+ */
3393
+ dAfterPrefix(p: i32): i32 {
3394
+ let off: i32 = this.dStartAt(DFA_UNANCHORED, this.dMaskF[CTX_OTHER], p)
3395
+ const n: i32 = toI32(this.prefix.length)
3396
+ let k: i32 = 0
3397
+ while (off >= 0 && k < n) {
3398
+ const c: i32 = this.dCls[toI32(this.prefix.charCodeAt(k))]
3399
+ let e: i32 = this.dTrans[off + c]
3400
+ if (e === 0) {
3401
+ e = this.dStep(off, c, p + k)
3402
+ }
3403
+ off = e > 0 ? e : -1
3404
+ k += 1
3405
+ }
3406
+ if (off < 0) {
3407
+ this.dPrefFast = false
3408
+ return -1
3409
+ }
3410
+ this.dPrefOff = off + 1
3411
+ return off
3412
+ }
3413
+
3414
+ /** The kind of the character before byte `p`, as the forward program tells kinds apart. */
3415
+ ctxBefore(text: string, p: i32): i32 {
3416
+ let kind: i32 = CTX_OTHER
3417
+ if (p <= 0) {
3418
+ kind = CTX_START
3419
+ } else {
3420
+ const b: i32 = toI32(text.charCodeAt(p - 1))
3421
+ if (b === 10 || b === 13) {
3422
+ kind = CTX_LINE
3423
+ } else if (isRegexWordByte(b)) {
3424
+ kind = CTX_WORD
3425
+ } else if (
3426
+ (b === 0xa8 || b === 0xa9) &&
3427
+ p >= 3 &&
3428
+ toI32(text.charCodeAt(p - 2)) === 0x80 &&
3429
+ toI32(text.charCodeAt(p - 3)) === 0xe2
3430
+ ) {
3431
+ kind = CTX_LINE
3432
+ }
3433
+ }
3434
+ return this.dMaskF[kind]
3435
+ }
3436
+
3437
+ /** The kind of the character at byte `p`, as the reverse program tells kinds apart. */
3438
+ ctxAt(text: string, p: i32): i32 {
3439
+ const len: i32 = toI32(text.length)
3440
+ let kind: i32 = CTX_OTHER
3441
+ if (p >= len) {
3442
+ kind = CTX_START
3443
+ } else {
3444
+ const b: i32 = toI32(text.charCodeAt(p))
3445
+ if (b === 10 || b === 13) {
3446
+ kind = CTX_LINE
3447
+ } else if (isRegexWordByte(b)) {
3448
+ kind = CTX_WORD
3449
+ } else if (b === 0xe2 && p + 2 < len && toI32(text.charCodeAt(p + 1)) === 0x80) {
3450
+ const b2: i32 = toI32(text.charCodeAt(p + 2))
3451
+ if (b2 === 0xa8 || b2 === 0xa9) {
3452
+ kind = CTX_LINE
3453
+ }
3454
+ }
3455
+ }
3456
+ return this.dMaskR[kind]
3457
+ }
3458
+
3459
+ /**
3460
+ * The forward DFA: where the leftmost-first match at or after `from` ends,
3461
+ * or, with `first`, where the earliest-ending match does — enough to know
3462
+ * there is one. `pinned` tries only a match that starts at `from`. Answers
3463
+ * `DFA_NONE` or `DFA_GAVE_UP` otherwise, and leaves where it stopped
3464
+ * reading in `dStop`.
3465
+ */
3466
+ dfaForward(text: string, from: i32, first: boolean, pinned: boolean): i32 {
3467
+ const len: i32 = toI32(text.length)
3468
+ const mode: i32 = this.anchored || pinned ? DFA_ANCHORED : DFA_UNANCHORED
3469
+ let p: i32 = from
3470
+ this.dStop = len
3471
+ let off: i32 = -1
3472
+ if (this.dSkip && !pinned) {
3473
+ p = this.dSkipTo(text, p)
3474
+ if (p < 0) {
3475
+ return DFA_NONE
3476
+ }
3477
+ if (this.dPrefFast) {
3478
+ // The prefix is at `p`: start past it.
3479
+ off = this.dPrefOff > 0 ? this.dPrefOff - 1 : this.dAfterPrefix(p)
3480
+ if (off >= 0) {
3481
+ p += toI32(this.prefix.length)
3482
+ }
3483
+ }
3484
+ }
3485
+ if (off < 0) {
3486
+ off = this.dStartAt(mode, this.dCtxFree ? this.dMaskF[CTX_OTHER] : this.ctxBefore(text, p), p)
3487
+ if (off < 0) {
3488
+ return DFA_GAVE_UP
3489
+ }
3490
+ }
3491
+ let end: i32 = DFA_NONE
3492
+ let trans: i32[] = this.dTrans
3493
+ const cls: i32[] = this.dCls
3494
+ while (true) {
3495
+ // The hot loop: an ASCII byte and a transition already computed.
3496
+ while (p >= 0 && p < len) {
3497
+ const b: i32 = toI32(text.charCodeAt(p))
3498
+ const e: i32 = uncheckedGet(trans, off | uncheckedGet(cls, b))
3499
+ if (e > 0) {
3500
+ off = e
3501
+ p += 1
3502
+ } else if (e === 0 || (-e & (DFA_DEAD | DFA_IDLE)) !== 0) {
3503
+ break
3504
+ } else {
3505
+ // A match ends before this byte and the search goes on.
3506
+ if (first) {
3507
+ this.dStop = p
3508
+ return p
3509
+ }
3510
+ end = p
3511
+ off = -e >> 4
3512
+ p += 1
3513
+ }
3514
+ }
3515
+ let c: i32 = this.dEot
3516
+ let w: i32 = 1
3517
+ if (p >= 0 && p < len) {
3518
+ const b: i32 = toI32(text.charCodeAt(p))
3519
+ if (b < 128) {
3520
+ // `dCls` has an entry for every byte.
3521
+ c = uncheckedGet(cls, b)
3522
+ } else {
3523
+ const packed: i32 = regexDecodeAt(text, p, len)
3524
+ c = this.dWideClass(packed >> 3)
3525
+ w = packed & 7
3526
+ }
3527
+ }
3528
+ let e: i32 = trans[off + c]
3529
+ if (e === 0) {
3530
+ e = this.dStep(off, c, p)
3531
+ if (e === 0) {
3532
+ return DFA_GAVE_UP
3533
+ }
3534
+ trans = this.dTrans
3535
+ }
3536
+ if (e > 0) {
3537
+ if (p >= len) {
3538
+ this.dStop = p
3539
+ return end
3540
+ }
3541
+ off = e
3542
+ p += w
3543
+ continue
3544
+ }
3545
+ e = -e
3546
+ if ((e & DFA_MATCH) !== 0) {
3547
+ if (first) {
3548
+ this.dStop = p
3549
+ return p
3550
+ }
3551
+ end = p
3552
+ }
3553
+ if ((e & DFA_DEAD) !== 0 || p >= len) {
3554
+ this.dStop = p
3555
+ return end
3556
+ }
3557
+ off = e >> 4
3558
+ p += w
3559
+ if ((e & DFA_IDLE) !== 0) {
3560
+ // No thread is alive and no match has been seen: skip to where the
3561
+ // next one can start. The state reached is the start state there
3562
+ // when the skip stays put or no assertion reads the context.
3563
+ const q: i32 = this.dSkipTo(text, p)
3564
+ if (q < 0) {
3565
+ return end
3566
+ }
3567
+ if (q !== p && !this.dCtxFree) {
3568
+ off = this.dStartAt(DFA_UNANCHORED, this.ctxBefore(text, q), q)
3569
+ if (off < 0) {
3570
+ return DFA_GAVE_UP
3571
+ }
3572
+ trans = this.dTrans
3573
+ }
3574
+ p = q
3575
+ if (this.dPrefOff > 0 && this.dPrefFast) {
3576
+ off = this.dPrefOff - 1
3577
+ p = q + toI32(this.prefix.length)
3578
+ }
3579
+ }
3580
+ }
3581
+ }
3582
+
3583
+ /**
3584
+ * The reverse DFA: where the match that ends at `e` starts, the furthest
3585
+ * back at or after `from`, or in `DFA_INNER` mode where the part before
3586
+ * `innerLit` does when the literal is at `e`. Answers `DFA_NONE` or
3587
+ * `DFA_GAVE_UP` otherwise.
3588
+ */
3589
+ dfaReverse(text: string, from: i32, e: i32, mode: i32): i32 {
3590
+ const len: i32 = toI32(text.length)
3591
+ let p: i32 = e
3592
+ let off: i32 = this.dStartAt(mode, this.ctxAt(text, e), e)
3593
+ if (off < 0) {
3594
+ return DFA_GAVE_UP
3595
+ }
3596
+ let s: i32 = DFA_NONE
3597
+ let trans: i32[] = this.dTrans
3598
+ const cls: i32[] = this.dCls
3599
+ while (true) {
3600
+ while (p > from) {
3601
+ const b: i32 = toI32(text.charCodeAt(p - 1))
3602
+ const t: i32 = uncheckedGet(trans, off | uncheckedGet(cls, b))
3603
+ if (t > 0) {
3604
+ off = t
3605
+ p -= 1
3606
+ } else if (t === 0 || (-t & DFA_DEAD) !== 0) {
3607
+ break
3608
+ } else {
3609
+ if ((-t & DFA_MATCH) !== 0) {
3610
+ s = p
3611
+ }
3612
+ off = -t >> 4
3613
+ p -= 1
3614
+ }
3615
+ }
3616
+ // The character before `p`. At `from` it is read only for its kind,
3617
+ // so it is decoded from the start of the text.
3618
+ let c: i32 = this.dEot
3619
+ let w: i32 = 1
3620
+ if (p > 0) {
3621
+ const b: i32 = toI32(text.charCodeAt(p - 1))
3622
+ if (b < 128) {
3623
+ // `dCls` has an entry for every byte.
3624
+ c = uncheckedGet(cls, b)
3625
+ } else {
3626
+ const lim: i32 = p > from ? from : 0
3627
+ let cp: i32 = REGEX_INVALID + b
3628
+ let k: i32 = 2
3629
+ while (k <= 4 && p - k >= lim) {
3630
+ const lead: i32 = toI32(text.charCodeAt(p - k))
3631
+ if (lead >= 0xc2) {
3632
+ const packed: i32 = regexDecodeAt(text, p - k, len)
3633
+ if ((packed & 7) === k) {
3634
+ cp = packed >> 3
3635
+ w = k
3636
+ }
3637
+ break
3638
+ }
3639
+ if (lead < 0x80 || lead >= 0xc0) {
3640
+ break
3641
+ }
3642
+ k += 1
3643
+ }
3644
+ c = this.dWideClass(cp)
3645
+ }
3646
+ }
3647
+ let t: i32 = trans[off + c]
3648
+ if (t === 0) {
3649
+ t = this.dStep(off, c, p)
3650
+ if (t === 0) {
3651
+ return DFA_GAVE_UP
3652
+ }
3653
+ trans = this.dTrans
3654
+ }
3655
+ if (t > 0) {
3656
+ if (p <= from) {
3657
+ return s
3658
+ }
3659
+ off = t
3660
+ p -= w
3661
+ continue
3662
+ }
3663
+ t = -t
3664
+ if ((t & DFA_MATCH) !== 0) {
3665
+ s = p
3666
+ }
3667
+ if ((t & DFA_DEAD) !== 0 || p <= from) {
3668
+ return s
3669
+ }
3670
+ off = t >> 4
3671
+ p -= w
3672
+ }
3673
+ }
3674
+
3675
+ /**
3676
+ * The leftmost-first match at or after `from`, found by `innerLit`: its end,
3677
+ * with its start in `dBegin`, or with `first` the earliest end of a match
3678
+ * that starts there. Answers `DFA_NONE` or `DFA_GAVE_UP` otherwise.
3679
+ *
3680
+ * The part before the literal cannot read its first byte, so a match that
3681
+ * starts at or before the literal's first occurrence `i` holds that
3682
+ * occurrence, and one that starts after it, a later one. The reverse DFA
3683
+ * finds the furthest-back start of the part before `i`; whether a match
3684
+ * goes on from the literal does not depend on that start, so the forward
3685
+ * DFA from it either finds the match or rules out every start up to `i`.
3686
+ * That forward search can read past `i` in vain each time, so the bytes it
3687
+ * wastes are counted against those the search moves past, over the life of
3688
+ * the regex: past twice those and a slack, the inner search is dropped for
3689
+ * the plain one, which keeps the cost linear.
3690
+ */
3691
+ dfaInner(text: string, from: i32, first: boolean): i32 {
3692
+ let lower: i32 = from
3693
+ while (this.innerLit !== "") {
3694
+ const i: i32 = toI32(text.indexOf(this.innerLit, lower))
3695
+ if (i < 0) {
3696
+ return DFA_NONE
3697
+ }
3698
+ this.innerGain += toF64(i + 1 - lower)
3699
+ const s: i32 = this.dfaReverse(text, lower, i, DFA_INNER)
3700
+ if (s === DFA_GAVE_UP) {
3701
+ return DFA_GAVE_UP
3702
+ }
3703
+ if (s >= 0) {
3704
+ const e: i32 = this.dfaForward(text, s, first, true)
3705
+ if (e !== DFA_NONE) {
3706
+ this.dBegin = s
3707
+ return e
3708
+ }
3709
+ this.innerWaste += toF64(this.dStop - i)
3710
+ if (this.innerWaste > 2.0 * this.innerGain + DFA_INNER_SLACK) {
3711
+ this.innerLit = ""
3712
+ }
3713
+ }
3714
+ lower = i + 1
3715
+ }
3716
+ // No match starts before `lower`.
3717
+ const e: i32 = this.dfaForward(text, lower, first, false)
3718
+ if (e < 0) {
3719
+ return e
3720
+ }
3721
+ this.dBegin = this.dfaReverse(text, lower, e, DFA_REVERSE)
3722
+ return this.dBegin < 0 ? DFA_GAVE_UP : e
3723
+ }
3724
+
3725
+ /**
3726
+ * Whether the pattern matches `text` at or after byte `from`, and the
3727
+ * leftmost-first match's first `want` slots in `mslots`: the DFA finds
3728
+ * whether there is one and where it ends, the reverse DFA where it starts,
3729
+ * and the VM, from that start, the groups.
3730
+ */
3731
+ run(text: string, from: i32, want: i32): boolean {
3732
+ const len: i32 = toI32(text.length)
3733
+ if (from < 0 || from > len) {
3734
+ return false
3735
+ }
3736
+ if (this.literal) {
3737
+ const at: i32 = this.findPrefix(text, from)
3738
+ if (at < 0) {
3739
+ return false
3740
+ }
3741
+ if (want >= REGEX_WANT_BOUNDS) {
3742
+ this.mslots[0] = at
3743
+ this.mslots[1] = at + toI32(this.prefix.length)
3744
+ }
3745
+ return true
3746
+ }
3747
+ if (!this.dReady) {
3748
+ this.dfaInit()
3749
+ }
3750
+ if (this.dOk && this.innerLit !== "") {
3751
+ const e: i32 = this.dfaInner(text, from, want === REGEX_WANT_NONE)
3752
+ if (e === DFA_NONE) {
3753
+ return false
3754
+ }
3755
+ if (e >= 0 && want <= REGEX_WANT_BOUNDS) {
3756
+ if (want > 0) {
3757
+ this.mslots[0] = this.dBegin
3758
+ this.mslots[1] = e
3759
+ }
3760
+ return true
3761
+ }
3762
+ if (e >= 0) {
3763
+ return this.vm(text, this.dBegin, want, true)
3764
+ }
3765
+ } else if (this.dOk) {
3766
+ if (want === REGEX_WANT_NONE) {
3767
+ const e: i32 = this.dfaForward(text, from, true, false)
3768
+ if (e !== DFA_GAVE_UP) {
3769
+ return e >= 0
3770
+ }
3771
+ } else if (this.revOk) {
3772
+ const e: i32 = this.dfaForward(text, from, false, false)
3773
+ if (e === DFA_NONE) {
3774
+ return false
3775
+ }
3776
+ if (e >= 0) {
3777
+ const s: i32 = this.dfaReverse(text, from, e, DFA_REVERSE)
3778
+ if (s >= 0 && want <= REGEX_WANT_BOUNDS) {
3779
+ this.mslots[0] = s
3780
+ this.mslots[1] = e
3781
+ return true
3782
+ }
3783
+ if (s >= 0) {
3784
+ return this.vm(text, s, want, true)
3785
+ }
3786
+ }
3787
+ }
3788
+ }
3789
+ return this.vm(text, from, want, false)
3790
+ }
3791
+
3792
+ /**
3793
+ * The VM: whether the pattern matches `text` at or after byte `from`, and
3794
+ * the leftmost-first match's first `want` slots in `mslots`. `want` is 0 to
3795
+ * learn only whether there is a match, 2 for its bounds, or every group's.
3796
+ * Any slots at all bring the marks along, because which match comes first
3797
+ * depends on them. `pinned` tries only a match that starts at `from`.
3798
+ */
3799
+ vm(text: string, from: i32, want: i32, pinned: boolean): boolean {
3800
+ const ns: i32 = want > 0 && this.width > 2 * this.groups ? this.width : want
3801
+ const len: i32 = toI32(text.length)
3802
+ const anchored: boolean = this.anchored || pinned
3803
+ const hasPrefix: boolean = this.prefix !== ""
3804
+ const hasScan: boolean = this.scanSel
3805
+ const hasSet: boolean = toI32(this.firstSet.length) > 0
3806
+ this.cCount = 0
3807
+ let matched: boolean = false
3808
+ let p: i32 = from
3809
+ while (true) {
3810
+ if (!matched && (p === from || !anchored)) {
3811
+ if (this.cCount === 0) {
3812
+ if (!anchored) {
3813
+ if (hasPrefix) {
3814
+ const at: i32 = this.findPrefix(text, p)
3815
+ if (at < 0) {
3816
+ break
3817
+ }
3818
+ p = at
3819
+ } else if (hasScan) {
3820
+ const at: i32 = this.scanTo(text, p)
3821
+ if (at < 0) {
3822
+ break
3823
+ }
3824
+ p = at
3825
+ } else if (hasSet) {
3826
+ // Byte by byte, so it stops on the first byte of a character
3827
+ // before any byte inside one.
3828
+ while (p >= 0 && p < len) {
3829
+ const b: i32 = toI32(text.charCodeAt(p))
3830
+ if (b >= 128 ? this.firstWide : this.firstSet[b] === 1) {
3831
+ break
3832
+ }
3833
+ p += 1
3834
+ }
3835
+ if (p >= len) {
3836
+ break
3837
+ }
3838
+ }
3839
+ }
3840
+ // The closure starts afresh at a new byte.
3841
+ this.newGeneration()
3842
+ }
3843
+ let k: i32 = 0
3844
+ while (k < ns) {
3845
+ this.caps[k] = -1
3846
+ k += 1
3847
+ }
3848
+ this.cCount = this.addThread(this.cDense, this.cSlots, this.cCount, REGEX_ENTRY, text, p, len, ns)
3849
+ }
3850
+ if (this.cCount === 0) {
3851
+ if (matched || anchored || p >= len) {
3852
+ break
3853
+ }
3854
+ p += regexWidthAt(text, p, len)
3855
+ continue
3856
+ }
3857
+ let cp: i32 = -1
3858
+ let w: i32 = 1
3859
+ if (p >= 0 && p < len) {
3860
+ cp = toI32(text.charCodeAt(p))
3861
+ if (cp >= 128) {
3862
+ const packed: i32 = regexDecodeAt(text, p, len)
3863
+ cp = packed >> 3
3864
+ w = packed & 7
3865
+ }
3866
+ }
3867
+ this.newGeneration()
3868
+ this.nCount = 0
3869
+ let i: i32 = 0
3870
+ while (i < this.cCount) {
3871
+ const pc: i32 = this.cDense[i]
3872
+ i += 1
3873
+ const o: i32 = this.op[pc]
3874
+ let hit: boolean = false
3875
+ if (o === OP_CHAR) {
3876
+ hit = cp === this.ia[pc] || cp === this.ib[pc]
3877
+ } else if (o === OP_CLASS) {
3878
+ if (cp >= 0 && cp < 128) {
3879
+ hit = this.clsAscii[this.ia[pc] * 128 + cp] === 1
3880
+ } else {
3881
+ hit = cp >= 0 && this.classHas(this.ia[pc], cp)
3882
+ }
3883
+ } else if (o === OP_ANY_NL) {
3884
+ hit = cp >= 0 && cp !== 10 && cp !== 13 && cp !== 0x2028 && cp !== 0x2029
3885
+ } else if (o === OP_ANY) {
3886
+ hit = cp >= 0
3887
+ } else if (o === OP_MATCH) {
3888
+ if (ns === 0) {
3889
+ return true
3890
+ }
3891
+ const base: i32 = pc * ns
3892
+ let k: i32 = 0
3893
+ while (k < ns) {
3894
+ this.mslots[k] = this.cSlots[base + k]
3895
+ k += 1
3896
+ }
3897
+ matched = true
3898
+ // Every thread after this one has lower priority.
3899
+ break
3900
+ }
3901
+ if (hit) {
3902
+ const base: i32 = pc * ns
3903
+ let k: i32 = 0
3904
+ while (k < ns) {
3905
+ this.caps[k] = this.cSlots[base + k]
3906
+ k += 1
3907
+ }
3908
+ this.nCount = this.addThread(this.nDense, this.nSlots, this.nCount, pc + 1, text, p + w, len, ns)
3909
+ }
3910
+ }
3911
+ const d: i32[] = this.cDense
3912
+ this.cDense = this.nDense
3913
+ this.nDense = d
3914
+ const t: i32[] = this.cSlots
3915
+ this.cSlots = this.nSlots
3916
+ this.nSlots = t
3917
+ this.cCount = this.nCount
3918
+ if (p >= len) {
3919
+ break
3920
+ }
3921
+ p += w
3922
+ }
3923
+ return matched
3924
+ }
3925
+
3926
+ /** Whether the pattern matches anywhere in `text`. */
3927
+ test(text: string): boolean {
3928
+ return this.run(text, REGEX_TEXT_START, REGEX_WANT_NONE)
3929
+ }
3930
+
3931
+ /**
3932
+ * Search `text` from byte `from` and fill `m` with the first match and
3933
+ * every group. Answers whether there was one; `m` is untouched when not.
3934
+ * Reusing one `RegexMatch` makes a loop of searches allocate nothing.
3935
+ */
3936
+ exec(text: string, from: i32, m: RegexMatch): boolean {
3937
+ const ns: i32 = 2 * this.groups
3938
+ if (!this.run(text, from, ns)) {
3939
+ return false
3940
+ }
3941
+ if (toI32(m.slots.length) !== ns) {
3942
+ m.slots = new Array<i32>(ns)
3943
+ }
3944
+ let k: i32 = 0
3945
+ while (k < ns) {
3946
+ m.slots[k] = this.mslots[k]
3947
+ k += 1
3948
+ }
3949
+ m.input = text
3950
+ m.names = this.names
3951
+ return true
3952
+ }
3953
+
3954
+ /** The first match at or after byte `from`, or null. */
3955
+ find(text: string, from: i32): RegexMatch | null {
3956
+ const m: RegexMatch = new RegexMatch()
3957
+ if (this.exec(text, from, m)) {
3958
+ return m
3959
+ }
3960
+ return null
3961
+ }
3962
+
3963
+ /** The text of every match, left to right, none overlapping. */
3964
+ findAll(text: string): string[] {
3965
+ const out: string[] = []
3966
+ const len: i32 = toI32(text.length)
3967
+ let pos: i32 = 0
3968
+ while (pos <= len && this.run(text, pos, REGEX_WANT_BOUNDS)) {
3969
+ const s: i32 = this.mslots[0]
3970
+ const e: i32 = this.mslots[1]
3971
+ out.push(text.slice(s, e))
3972
+ if (e === s) {
3973
+ if (e >= len) {
3974
+ break
3975
+ }
3976
+ pos = e + regexWidthAt(text, e, len)
3977
+ } else {
3978
+ pos = e
3979
+ }
3980
+ }
3981
+ return out
3982
+ }
3983
+
3984
+ /** How many matches `findAll` would answer. */
3985
+ count(text: string): i32 {
3986
+ const len: i32 = toI32(text.length)
3987
+ let n: i32 = 0
3988
+ let pos: i32 = 0
3989
+ if (this.literal && this.prefix !== "") {
3990
+ const n0: i32 = toI32(this.prefix.length)
3991
+ while (true) {
3992
+ const at: i32 = this.findPrefix(text, pos)
3993
+ if (at < 0) {
3994
+ return n
3995
+ }
3996
+ n += 1
3997
+ pos = at + n0
3998
+ }
3999
+ }
4000
+ if (!this.dReady) {
4001
+ this.dfaInit()
4002
+ }
4003
+ // A match that cannot be empty ends past where its search started, so
4004
+ // the forward DFA alone counts them; marks could move the end.
4005
+ if (this.dOk && !this.literal && !this.nullable && this.width === 2 * this.groups) {
4006
+ while (pos <= len) {
4007
+ let e: i32 = DFA_NONE
4008
+ if (this.innerLit !== "") {
4009
+ e = this.dfaInner(text, pos, false)
4010
+ } else {
4011
+ e = this.dfaForward(text, pos, false, false)
4012
+ }
4013
+ if (e === DFA_NONE) {
4014
+ return n
4015
+ }
4016
+ if (e === DFA_GAVE_UP) {
4017
+ break
4018
+ }
4019
+ n += 1
4020
+ pos = e
4021
+ }
4022
+ }
4023
+ while (pos <= len && this.run(text, pos, REGEX_WANT_BOUNDS)) {
4024
+ n += 1
4025
+ const s: i32 = this.mslots[0]
4026
+ const e: i32 = this.mslots[1]
4027
+ if (e === s) {
4028
+ if (e >= len) {
4029
+ break
4030
+ }
4031
+ pos = e + regexWidthAt(text, e, len)
4032
+ } else {
4033
+ pos = e
4034
+ }
4035
+ }
4036
+ return n
4037
+ }
4038
+
4039
+ /**
4040
+ * `text` with its first match replaced by `replacement`, which is
4041
+ * `String.prototype.replace`'s template: `$$` is `$`, `$&` the match,
4042
+ * `` $` `` and `$'` the text before and after it, `$1` to `$99` a group
4043
+ * and `$<name>` a named one; a group that took no part is "".
4044
+ */
4045
+ replace(text: string, replacement: string): string {
4046
+ return this.replaceMatches(text, replacement, false)
4047
+ }
4048
+
4049
+ /** `text` with every match replaced by `replacement`, as `replace` reads it. */
4050
+ replaceAll(text: string, replacement: string): string {
4051
+ return this.replaceMatches(text, replacement, true)
4052
+ }
4053
+
4054
+ replaceMatches(text: string, replacement: string, all: boolean): string {
4055
+ const len: i32 = toI32(text.length)
4056
+ // Track every group only when the template can name one.
4057
+ let ns: i32 = 2
4058
+ let i: i32 = toI32(replacement.indexOf("$"))
4059
+ while (i >= 0 && i + 1 < toI32(replacement.length)) {
4060
+ const c: i32 = toI32(replacement.charCodeAt(i + 1))
4061
+ if ((c >= 48 && c <= 57) || c === 60) {
4062
+ ns = 2 * this.groups
4063
+ break
4064
+ }
4065
+ i = toI32(replacement.indexOf("$", i + 2))
4066
+ }
4067
+ const parts: string[] = []
4068
+ let last: i32 = 0
4069
+ let pos: i32 = 0
4070
+ while (pos <= len && this.run(text, pos, ns)) {
4071
+ const s: i32 = this.mslots[0]
4072
+ const e: i32 = this.mslots[1]
4073
+ parts.push(text.slice(last, s))
4074
+ this.expand(replacement, text, ns, parts)
4075
+ last = e
4076
+ if (!all) {
4077
+ break
4078
+ }
4079
+ if (e === s) {
4080
+ if (e >= len) {
4081
+ break
4082
+ }
4083
+ pos = e + regexWidthAt(text, e, len)
4084
+ } else {
4085
+ pos = e
4086
+ }
4087
+ }
4088
+ if (toI32(parts.length) === 0) {
4089
+ return text
4090
+ }
4091
+ parts.push(text.slice(last, len))
4092
+ return parts.join("")
4093
+ }
4094
+
4095
+ /** Group `k` of the match in `mslots`, or "". */
4096
+ slotText(text: string, ns: i32, k: i32): string {
4097
+ if (k < 0 || 2 * k + 1 >= ns || this.mslots[2 * k] < 0) {
4098
+ return ""
4099
+ }
4100
+ return text.slice(this.mslots[2 * k], this.mslots[2 * k + 1])
4101
+ }
4102
+
4103
+ /** Append the replacement template, expanded for the match in `mslots`, to `parts`. */
4104
+ expand(rep: string, text: string, ns: i32, parts: string[]): void {
4105
+ const n: i32 = toI32(rep.length)
4106
+ const groupsAfterZero: i32 = this.groups - 1
4107
+ let lit: i32 = 0
4108
+ let i: i32 = toI32(rep.indexOf("$"))
4109
+ while (i >= 0 && i + 1 < n) {
4110
+ const c: i32 = toI32(rep.charCodeAt(i + 1))
4111
+ let used: i32 = 0
4112
+ let value: string = ""
4113
+ if (c === 36) {
4114
+ value = "$"
4115
+ used = 2
4116
+ } else if (c === 38) {
4117
+ value = this.slotText(text, ns, REGEX_WHOLE)
4118
+ used = 2
4119
+ } else if (c === 96) {
4120
+ value = text.slice(0, this.mslots[0])
4121
+ used = 2
4122
+ } else if (c === 39) {
4123
+ value = text.slice(this.mslots[1], toI32(text.length))
4124
+ used = 2
4125
+ } else if (c >= 48 && c <= 57) {
4126
+ const one: i32 = c - 48
4127
+ const d: i32 = i + 2 < n ? toI32(rep.charCodeAt(i + 2)) : -1
4128
+ if (d >= 48 && d <= 57 && one * 10 + (d - 48) >= 1 && one * 10 + (d - 48) <= groupsAfterZero) {
4129
+ value = this.slotText(text, ns, one * 10 + (d - 48))
4130
+ used = 3
4131
+ } else if (one >= 1 && one <= groupsAfterZero) {
4132
+ value = this.slotText(text, ns, one)
4133
+ used = 2
4134
+ }
4135
+ } else if (c === 60 && this.hasNames) {
4136
+ const close: i32 = toI32(rep.indexOf(">", i + 2))
4137
+ if (close >= 0) {
4138
+ value = this.slotText(text, ns, this.groupIndex(rep.slice(i + 2, close)))
4139
+ used = close + 1 - i
4140
+ }
4141
+ }
4142
+ if (used === 0) {
4143
+ i = toI32(rep.indexOf("$", i + 1))
4144
+ continue
4145
+ }
4146
+ parts.push(rep.slice(lit, i))
4147
+ parts.push(value)
4148
+ lit = i + used
4149
+ i = toI32(rep.indexOf("$", lit))
4150
+ }
4151
+ parts.push(rep.slice(lit, n))
4152
+ }
4153
+
4154
+ /**
4155
+ * `text` cut at every match, as `String.prototype.split` cuts it: each
4156
+ * group's text follows the piece before its match ("" for one that took no
4157
+ * part), an empty match where the last piece ended or at the end cuts
4158
+ * nothing, and "" splits to nothing when the pattern matches it, else to
4159
+ * `[""]`.
4160
+ */
4161
+ split(text: string): string[] {
4162
+ const len: i32 = toI32(text.length)
4163
+ const out: string[] = []
4164
+ const ns: i32 = 2 * this.groups
4165
+ if (len === 0) {
4166
+ if (!this.run(text, REGEX_TEXT_START, REGEX_WANT_NONE)) {
4167
+ out.push(text)
4168
+ }
4169
+ return out
4170
+ }
4171
+ let p: i32 = 0
4172
+ let q: i32 = 0
4173
+ while (q < len && this.run(text, q, ns)) {
4174
+ const s: i32 = this.mslots[0]
4175
+ const e: i32 = this.mslots[1]
4176
+ if (s >= len) {
4177
+ break
4178
+ }
4179
+ if (e === p) {
4180
+ q = s + regexWidthAt(text, s, len)
4181
+ continue
4182
+ }
4183
+ out.push(text.slice(p, s))
4184
+ let k: i32 = 1
4185
+ while (k < this.groups) {
4186
+ out.push(this.slotText(text, ns, k))
4187
+ k += 1
4188
+ }
4189
+ p = e
4190
+ q = p
4191
+ }
4192
+ out.push(text.slice(p, len))
4193
+ return out
4194
+ }
4195
+ }
4196
+
4197
+ /**
4198
+ * Compile `pattern` with `flags`, a string of `i`, `m` and `s` in any order.
4199
+ * The error names what is wrong and its byte in the pattern.
4200
+ */
4201
+ export const regexCompile = (pattern: string, flags: string): Result<Regex, string> => {
4202
+ let icase: boolean = false
4203
+ let multiline: boolean = false
4204
+ let dotAll: boolean = false
4205
+ const nflags: i32 = toI32(flags.length)
4206
+ let i: i32 = 0
4207
+ while (i < nflags) {
4208
+ const c: i32 = toI32(flags.charCodeAt(i))
4209
+ if (c === 105 && !icase) {
4210
+ icase = true
4211
+ } else if (c === 109 && !multiline) {
4212
+ multiline = true
4213
+ } else if (c === 115 && !dotAll) {
4214
+ dotAll = true
4215
+ } else {
4216
+ return Err(`regex: invalid flags "${flags}"`)
4217
+ }
4218
+ i += 1
4219
+ }
4220
+ const p: RegexParser = new RegexParser(pattern, icase, multiline, dotAll)
4221
+ const root: i32 = p.parseAlt(REGEX_TOP)
4222
+ if (!p.failed && p.pos < p.len) {
4223
+ p.fail("unmatched )", p.pos)
4224
+ }
4225
+ if (p.failed) {
4226
+ return Err(`regex: ${p.error} at byte ${p.errorAt}`)
4227
+ }
4228
+ const re: Regex = new Regex(pattern, flags)
4229
+ const error: string = re.build(p, root)
4230
+ if (error !== "") {
4231
+ return Err(`regex: ${error}`)
4232
+ }
4233
+ return Ok(re)
4234
+ }
4235
+
4236
+ /** `text` with every syntax character escaped, so that it compiles to a pattern matching exactly `text`. */
4237
+ export const regexEscape = (text: string): string => {
4238
+ const parts: string[] = []
4239
+ const n: i32 = toI32(text.length)
4240
+ // Where the text not yet copied starts; a syntax character at byte 0 leaves it at 0.
4241
+ let lit: i32 = 0
4242
+ let i: i32 = 0
4243
+ while (i < n) {
4244
+ const c: i32 = toI32(text.charCodeAt(i))
4245
+ // ^ $ \ . * + ? ( ) [ ] { } | /
4246
+ if (
4247
+ c === 94 ||
4248
+ c === 36 ||
4249
+ c === 92 ||
4250
+ c === 46 ||
4251
+ c === 42 ||
4252
+ c === 43 ||
4253
+ c === 63 ||
4254
+ c === 40 ||
4255
+ c === 41 ||
4256
+ c === 91 ||
4257
+ c === 93 ||
4258
+ c === 123 ||
4259
+ c === 125 ||
4260
+ c === 124 ||
4261
+ c === 47
4262
+ ) {
4263
+ parts.push(text.slice(lit, i))
4264
+ parts.push("\\")
4265
+ lit = i
4266
+ }
4267
+ i += 1
4268
+ }
4269
+ if (parts.length === 0) {
4270
+ return text
4271
+ }
4272
+ parts.push(text.slice(lit, n))
4273
+ return parts.join("")
4274
+ }