@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.
- package/INSTALL.md +58 -28
- package/bin/nish +0 -0
- package/docs/AI.md +2759 -0
- package/package.json +3 -2
- package/runtime/browser.d.ts +14 -0
- package/runtime/browser.mjs +88 -0
- package/runtime/core/arrays.d.ts +18 -0
- package/runtime/core/arrays.mjs +125 -0
- package/runtime/core/checks.mjs +20 -0
- package/runtime/core/host.d.ts +70 -0
- package/runtime/core/host.mjs +311 -0
- package/runtime/core/numbers.d.ts +53 -0
- package/runtime/core/numbers.mjs +316 -0
- package/runtime/core/result.d.ts +41 -0
- package/runtime/core/result.mjs +127 -0
- package/runtime/core/strings.d.ts +14 -0
- package/runtime/core/strings.mjs +169 -0
- package/runtime/core.d.ts +9 -0
- package/runtime/core.mjs +55 -0
- package/runtime/host.d.ts +6 -0
- package/runtime/nish.d.ts +99 -5
- package/runtime/nish.h +289 -19
- package/runtime/nish.mjs +39 -3
- package/runtime/node.mjs +383 -0
- package/runtime/runtime-aes.c +490 -0
- package/runtime/runtime-bytes.c +401 -0
- package/runtime/runtime-cross.c +366 -0
- package/runtime/runtime-host.c +8 -7
- package/runtime/runtime-net.c +93 -34
- package/runtime/runtime-os.c +214 -16
- package/runtime/runtime-parallel.c +590 -107
- package/runtime/runtime-simd.c +235 -11
- package/runtime/runtime-term.c +504 -0
- package/runtime/runtime-wasm.c +5 -14
- package/runtime/runtime.c +257 -17
- package/runtime/shim.mjs +6 -915
- package/runtime/sqlite/LICENSE.md +85 -0
- package/runtime/sqlite/README.md +45 -0
- package/runtime/sqlite/nish-sqlite.c +359 -0
- package/runtime/sqlite/sqlite3.c +271131 -0
- package/runtime/sqlite/sqlite3.h +14447 -0
- package/scripts/build.sh +437 -28
- package/std/LICENSE-fast-float +23 -0
- package/std/LICENSE-fdlibm +8 -0
- package/std/README.md +108 -58
- package/std/base64.ts +116 -0
- package/std/bytes.ts +419 -0
- package/std/cli.ts +328 -0
- package/std/collections.ts +6 -5
- package/std/crypto/LICENSE-crypt_blowfish +26 -0
- package/std/crypto/LICENSE-libsecp256k1 +19 -0
- package/std/crypto/aes.ts +238 -76
- package/std/crypto/base64url.ts +125 -0
- package/std/crypto/bcrypt.ts +760 -0
- package/std/crypto/chacha20poly1305.ts +43 -9
- package/std/crypto/ed25519.ts +840 -0
- package/std/crypto/hkdf.ts +304 -12
- package/std/crypto/hmac.ts +382 -54
- package/std/crypto/p256.ts +932 -61
- package/std/crypto/x25519.ts +55 -37
- package/std/crypto/x509-random.ts +71 -0
- package/std/crypto/x509.ts +20 -131
- package/std/json-tokenizer.ts +4237 -0
- package/std/json-typed.ts +1014 -0
- package/std/json-value.ts +1182 -0
- package/std/json.ts +1202 -34
- package/std/math.ts +608 -0
- package/std/net/buffer-pool.ts +151 -0
- package/std/net/hpack.ts +416 -112
- package/std/net/http-fields.ts +93 -0
- package/std/net/http1-server.ts +501 -62
- package/std/net/http1.ts +31 -6
- package/std/net/http2-tls.ts +112 -24
- package/std/net/http2.ts +333 -75
- package/std/net/http3-server.ts +261 -43
- package/std/net/http3.ts +187 -28
- package/std/net/postgres.ts +1875 -0
- package/std/net/prefork.ts +412 -0
- package/std/net/quic-listener.ts +108 -11
- package/std/net/quic-packet.ts +243 -82
- package/std/net/quic-recovery.ts +361 -30
- package/std/net/quic-stream.ts +424 -24
- package/std/net/quic.ts +519 -167
- package/std/net/router.ts +676 -0
- package/std/net/tls/codec.ts +426 -27
- package/std/net/tls/record-server.ts +478 -132
- package/std/net/tls/record.ts +133 -42
- package/std/net/tls/schedule.ts +464 -55
- package/std/net/tls/ticket.ts +610 -0
- package/std/net/tls-tcp.ts +196 -53
- package/std/net/tls.ts +1018 -260
- package/std/net/url.ts +571 -0
- package/std/random.ts +94 -0
- package/std/regex.ts +4274 -0
- package/std/sqlite.ts +391 -0
- package/std/term-io.ts +978 -0
- package/std/term-pixels.ts +874 -0
- package/std/term-probe.ts +459 -0
- package/std/term-rate.ts +993 -0
- package/std/term.ts +2453 -0
- package/std/text.ts +90 -7
- package/std/threads.ts +438 -14
- package/std/yaml-lines.ts +141 -0
- 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
|
+
}
|