@blamejs/blamejs-shop 0.5.19 → 0.5.21
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/CHANGELOG.md +4 -0
- package/README.md +1 -0
- package/SECURITY.md +17 -0
- package/lib/addresses.js +2 -2
- package/lib/admin.js +49 -45
- package/lib/affiliates.js +7 -23
- package/lib/announcement-bar.js +5 -22
- package/lib/api-keys.js +4 -13
- package/lib/asset-manifest.json +1 -1
- package/lib/banner-ab-tests.js +5 -9
- package/lib/blog-articles.js +8 -26
- package/lib/captcha-gate.js +7 -17
- package/lib/carrier-accounts.js +3 -10
- package/lib/catalog-drafts.js +5 -12
- package/lib/category-navigation.js +4 -20
- package/lib/cms-blocks.js +4 -7
- package/lib/compliance-export.js +4 -11
- package/lib/consent-ledger.js +2 -4
- package/lib/cost-layers.js +56 -21
- package/lib/customer-import.js +2 -2
- package/lib/customer-notes.js +7 -23
- package/lib/customer-roles.js +3 -5
- package/lib/customer-segments.js +17 -14
- package/lib/customer-surveys.js +7 -14
- package/lib/customers.js +2 -4
- package/lib/dispute-resolution.js +5 -13
- package/lib/dropship-forwarding.js +3 -5
- package/lib/dunning.js +2 -7
- package/lib/email-ab-tests.js +4 -8
- package/lib/email-campaigns.js +25 -14
- package/lib/email-warmup.js +2 -7
- package/lib/experiments.js +6 -10
- package/lib/externaldb-d1.js +78 -44
- package/lib/geolocation.js +18 -28
- package/lib/gift-options.js +5 -23
- package/lib/knowledge-base.js +7 -22
- package/lib/line-gift-wrap.js +3 -17
- package/lib/live-chat.js +5 -18
- package/lib/metered-usage.js +3 -8
- package/lib/notifications.js +3 -7
- package/lib/operator-accounts.js +3 -5
- package/lib/operator-approvals.js +6 -8
- package/lib/operator-audit-log.js +2 -7
- package/lib/operator-help-center.js +5 -21
- package/lib/operator-inbox.js +4 -11
- package/lib/operator-roles.js +5 -7
- package/lib/order-escalation.js +2 -2
- package/lib/order-export.js +24 -20
- package/lib/order-notes.js +4 -14
- package/lib/order-ratings.js +4 -10
- package/lib/payment-methods.js +5 -13
- package/lib/payment-retries.js +2 -7
- package/lib/payment.js +103 -58
- package/lib/pixel-events.js +8 -23
- package/lib/plan-changes.js +2 -7
- package/lib/product-qa.js +4 -8
- package/lib/promo-banners.js +7 -15
- package/lib/purchase-orders.js +3 -6
- package/lib/push-notifications.js +7 -32
- package/lib/pwa-manifest.js +6 -11
- package/lib/quotes.js +5 -9
- package/lib/referrals.js +10 -6
- package/lib/reviews.js +4 -10
- package/lib/robots-config.js +6 -14
- package/lib/sales-tax-filings.js +2 -2
- package/lib/search-facets.js +5 -5
- package/lib/search-ranking.js +4 -7
- package/lib/search-synonyms.js +7 -21
- package/lib/security-middleware.js +125 -70
- package/lib/seller-signup.js +10 -26
- package/lib/sidebar-widgets.js +6 -15
- package/lib/site-redirects.js +49 -137
- package/lib/sitemap-generator.js +3 -7
- package/lib/sms-dispatcher.js +7 -23
- package/lib/stock-receipts.js +4 -10
- package/lib/storefront-dashboards.js +2 -10
- package/lib/storefront-forms.js +9 -22
- package/lib/storefront-pages.js +6 -20
- package/lib/storefront.js +21 -23
- package/lib/subscription-billing.js +3 -8
- package/lib/subscription-controls.js +2 -7
- package/lib/suggestion-box.js +9 -26
- package/lib/support-tickets.js +6 -24
- package/lib/tax-remittance.js +2 -2
- package/lib/tenants.js +2 -7
- package/lib/text-guard.js +137 -35
- package/lib/theme-assets.js +4 -16
- package/lib/translations.js +3 -6
- package/lib/trust-badges.js +4 -7
- package/lib/vendor/MANIFEST.json +107 -97
- package/lib/vendor/blamejs/CHANGELOG.md +180 -0
- package/lib/vendor/blamejs/NOTICE +14 -10
- package/lib/vendor/blamejs/README.md +17 -5
- package/lib/vendor/blamejs/index.js +2 -0
- package/lib/vendor/blamejs/lib/acme.js +9 -9
- package/lib/vendor/blamejs/lib/api-snapshot.js +35 -16
- package/lib/vendor/blamejs/lib/app-shutdown.js +1 -1
- package/lib/vendor/blamejs/lib/archive-adapters.js +1 -1
- package/lib/vendor/blamejs/lib/archive.js +13 -24
- package/lib/vendor/blamejs/lib/auth/access-lock.js +2 -1
- package/lib/vendor/blamejs/lib/auth/bot-challenge.js +3 -6
- package/lib/vendor/blamejs/lib/auth/ciba.js +32 -3
- package/lib/vendor/blamejs/lib/auth/oauth.js +65 -8
- package/lib/vendor/blamejs/lib/auth/password.js +1 -1
- package/lib/vendor/blamejs/lib/auth/saml.js +21 -3
- package/lib/vendor/blamejs/lib/auth-bot-challenge.js +3 -3
- package/lib/vendor/blamejs/lib/backup/index.js +5 -5
- package/lib/vendor/blamejs/lib/case-fold-classes.js +491 -0
- package/lib/vendor/blamejs/lib/codepoint-class.js +131 -0
- package/lib/vendor/blamejs/lib/constants.js +79 -0
- package/lib/vendor/blamejs/lib/db-declare-view.js +8 -2
- package/lib/vendor/blamejs/lib/error-page.js +20 -1
- package/lib/vendor/blamejs/lib/external-db.js +5 -1
- package/lib/vendor/blamejs/lib/flag-evaluation-context.js +24 -6
- package/lib/vendor/blamejs/lib/flag.js +28 -2
- package/lib/vendor/blamejs/lib/framework-schema.js +14 -3
- package/lib/vendor/blamejs/lib/guard-regex.js +2345 -89
- package/lib/vendor/blamejs/lib/http-client.js +217 -40
- package/lib/vendor/blamejs/lib/mail-bimi.js +1 -1
- package/lib/vendor/blamejs/lib/mail-crypto-pgp.js +3 -2
- package/lib/vendor/blamejs/lib/mail-dav.js +26 -26
- package/lib/vendor/blamejs/lib/mail-deploy.js +10 -10
- package/lib/vendor/blamejs/lib/mail-scan.js +3 -0
- package/lib/vendor/blamejs/lib/mail-store.js +7 -1
- package/lib/vendor/blamejs/lib/mail.js +5 -1
- package/lib/vendor/blamejs/lib/middleware/age-gate.js +3 -2
- package/lib/vendor/blamejs/lib/middleware/api-encrypt.js +1 -1
- package/lib/vendor/blamejs/lib/middleware/assetlinks.js +2 -1
- package/lib/vendor/blamejs/lib/middleware/asyncapi-serve.js +3 -2
- package/lib/vendor/blamejs/lib/middleware/bearer-auth.js +2 -1
- package/lib/vendor/blamejs/lib/middleware/compression.js +1 -1
- package/lib/vendor/blamejs/lib/middleware/cors.js +1 -1
- package/lib/vendor/blamejs/lib/middleware/csp-report.js +5 -5
- package/lib/vendor/blamejs/lib/middleware/csrf-protect.js +2 -1
- package/lib/vendor/blamejs/lib/middleware/daily-byte-quota.js +2 -2
- package/lib/vendor/blamejs/lib/middleware/deny-response.js +2 -1
- package/lib/vendor/blamejs/lib/middleware/dpop.js +2 -2
- package/lib/vendor/blamejs/lib/middleware/fetch-metadata.js +2 -1
- package/lib/vendor/blamejs/lib/middleware/flag-context.js +17 -0
- package/lib/vendor/blamejs/lib/middleware/idempotency-key.js +10 -8
- package/lib/vendor/blamejs/lib/middleware/openapi-serve.js +3 -2
- package/lib/vendor/blamejs/lib/middleware/require-aal.js +3 -2
- package/lib/vendor/blamejs/lib/middleware/require-auth.js +4 -3
- package/lib/vendor/blamejs/lib/middleware/require-bound-key.js +4 -3
- package/lib/vendor/blamejs/lib/middleware/require-content-type.js +3 -2
- package/lib/vendor/blamejs/lib/middleware/require-methods.js +3 -2
- package/lib/vendor/blamejs/lib/middleware/require-mtls.js +3 -2
- package/lib/vendor/blamejs/lib/middleware/security-txt.js +2 -1
- package/lib/vendor/blamejs/lib/middleware/web-app-manifest.js +2 -1
- package/lib/vendor/blamejs/lib/network-dns-resolver.js +1 -1
- package/lib/vendor/blamejs/lib/network-dns.js +3 -3
- package/lib/vendor/blamejs/lib/network-nts.js +35 -14
- package/lib/vendor/blamejs/lib/network-proxy.js +42 -4
- package/lib/vendor/blamejs/lib/network-smtp-policy.js +1 -1
- package/lib/vendor/blamejs/lib/network-tls.js +341 -11
- package/lib/vendor/blamejs/lib/nist-crosswalk.js +2 -2
- package/lib/vendor/blamejs/lib/ntp-check.js +30 -19
- package/lib/vendor/blamejs/lib/object-store/azure-blob.js +1 -1
- package/lib/vendor/blamejs/lib/object-store/gcs.js +1 -1
- package/lib/vendor/blamejs/lib/object-store/http-put.js +2 -1
- package/lib/vendor/blamejs/lib/object-store/sigv4-bucket-ops.js +5 -5
- package/lib/vendor/blamejs/lib/object-store/sigv4.js +2 -2
- package/lib/vendor/blamejs/lib/observability-otlp-exporter.js +4 -2
- package/lib/vendor/blamejs/lib/openapi.js +1 -1
- package/lib/vendor/blamejs/lib/pqc-agent.js +4 -7
- package/lib/vendor/blamejs/lib/problem-details.js +2 -1
- package/lib/vendor/blamejs/lib/redis-client.js +12 -1
- package/lib/vendor/blamejs/lib/regex-linear.js +1349 -0
- package/lib/vendor/blamejs/lib/render.js +531 -7
- package/lib/vendor/blamejs/lib/request-helpers.js +181 -5
- package/lib/vendor/blamejs/lib/router.js +37 -13
- package/lib/vendor/blamejs/lib/safe-async.js +142 -0
- package/lib/vendor/blamejs/lib/safe-icap.js +3 -0
- package/lib/vendor/blamejs/lib/safe-sql.js +15 -3
- package/lib/vendor/blamejs/lib/self-update.js +2 -2
- package/lib/vendor/blamejs/lib/testing.js +54 -7
- package/lib/vendor/blamejs/lib/validate-opts.js +56 -0
- package/lib/vendor/blamejs/lib/vendor/MANIFEST.json +62 -25
- package/lib/vendor/blamejs/lib/vendor/blamejs-pki.cjs +2004 -142
- package/lib/vendor/blamejs/lib/vendor/browser/noble-ciphers.mjs +727 -0
- package/lib/vendor/blamejs/lib/vendor/browser/noble-hashes.mjs +1070 -0
- package/lib/vendor/blamejs/lib/vendor/browser/noble-post-quantum.mjs +1090 -0
- package/lib/vendor/blamejs/lib/vendor/noble-post-quantum.cjs +510 -227
- package/lib/vendor/blamejs/lib/webhook-hmac.js +19 -2
- package/lib/vendor/blamejs/lib/webhook.js +1 -1
- package/lib/vendor/blamejs/lib/ws-client.js +24 -2
- package/lib/vendor/blamejs/package.json +1 -1
- package/lib/vendor/blamejs/sbom.cdx.json +6 -6
- package/lib/vendor-invoices.js +3 -6
- package/lib/vendors.js +5 -19
- package/lib/webhook-receiver.js +46 -83
- package/lib/webhook-subscriptions.js +3 -13
- package/lib/wishlist-sharing.js +5 -10
- package/package.json +1 -1
|
@@ -0,0 +1,1349 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Copyright (c) blamejs contributors
|
|
3
|
+
"use strict";
|
|
4
|
+
/**
|
|
5
|
+
* @module b.regexLinear
|
|
6
|
+
* @nav Security
|
|
7
|
+
* @title Linear regex
|
|
8
|
+
* @order 70
|
|
9
|
+
* @slug regex-linear
|
|
10
|
+
*
|
|
11
|
+
* @intro
|
|
12
|
+
* Run an operator-supplied pattern in time proportional to the subject,
|
|
13
|
+
* whatever the pattern is.
|
|
14
|
+
*
|
|
15
|
+
* The backtracking engine every JavaScript runtime ships explores one path at
|
|
16
|
+
* a time and reconsiders when a path fails. For most patterns that is quick
|
|
17
|
+
* and for some it is catastrophic: `(a+)+$` against forty `a`s and a `!`
|
|
18
|
+
* takes longer than the request that carried it, and the pattern that does it
|
|
19
|
+
* is a few characters long. Where the pattern comes from an operator's
|
|
20
|
+
* configuration and the subject from a request, that is a denial of service
|
|
21
|
+
* with no payload.
|
|
22
|
+
*
|
|
23
|
+
* Screening the pattern first — `b.guardRegex` — decides whether a pattern
|
|
24
|
+
* LOOKS dangerous. It is a conservative screen, and conservative screens have
|
|
25
|
+
* two failure modes: they turn away patterns that were fine, and they cannot
|
|
26
|
+
* promise to catch every shape that is not.
|
|
27
|
+
*
|
|
28
|
+
* This runs the pattern instead. It simulates every path at once, one
|
|
29
|
+
* character of the subject at a time, keeping at most one live position per
|
|
30
|
+
* instruction. A path that dies costs nothing to abandon because it was never
|
|
31
|
+
* the only one being followed, and no input makes the work grow faster than
|
|
32
|
+
* the length of the subject times the size of the pattern. There is nothing
|
|
33
|
+
* to tune, no budget to exhaust and no shape to special-case.
|
|
34
|
+
*
|
|
35
|
+
* What it will not do is guess. Backreferences and lookaround cannot be
|
|
36
|
+
* simulated this way, so a pattern using them is refused by name at compile
|
|
37
|
+
* time rather than quietly handed to the engine that can be made to hang.
|
|
38
|
+
*
|
|
39
|
+
* @card
|
|
40
|
+
* Run an operator's regex in time proportional to the subject, whatever the
|
|
41
|
+
* pattern.
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
var C = require("./constants");
|
|
45
|
+
var codepointClass = require("./codepoint-class");
|
|
46
|
+
var frameworkError = require("./framework-error");
|
|
47
|
+
|
|
48
|
+
var RegexLinearError = frameworkError.defineClass("RegexLinearError", {
|
|
49
|
+
alwaysPermanent: true,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
var MAX_CODE_POINT = 0x10FFFF;
|
|
53
|
+
|
|
54
|
+
// A pattern longer than this is refused unread. Operators write patterns; this
|
|
55
|
+
// is far above any of them, and a compiled program grows with the source.
|
|
56
|
+
var MAX_SOURCE_BYTES = C.BYTES.kib(64);
|
|
57
|
+
|
|
58
|
+
// Bounded repeats are compiled by copying the body, so the counts an operator
|
|
59
|
+
// writes bound the program's size. `{1000000}` is a pattern that would build a
|
|
60
|
+
// program larger than the subject it screens.
|
|
61
|
+
var MAX_REPEAT_EXPANSION = 4096;
|
|
62
|
+
|
|
63
|
+
// And a ceiling on the compiled program however the counts are arranged, since
|
|
64
|
+
// nesting multiplies them. Far above any pattern an operator writes.
|
|
65
|
+
var MAX_PROGRAM_LENGTH = 65536;
|
|
66
|
+
|
|
67
|
+
// ---- character sets, as exact code-point ranges -----------------------------
|
|
68
|
+
//
|
|
69
|
+
// A set is a sorted, non-overlapping list of `[lo, hi]` code-point ranges. The
|
|
70
|
+
// analysis parser in `b.guardRegex` enumerates class members instead and widens
|
|
71
|
+
// to "anything" when a range is too large to enumerate, which is the right
|
|
72
|
+
// answer for an over-approximating screen and the wrong one here: an engine
|
|
73
|
+
// that matches a widened set matches a different language than the operator
|
|
74
|
+
// wrote. So this keeps ranges, and a class spanning the whole code space costs
|
|
75
|
+
// one of them rather than a member each.
|
|
76
|
+
|
|
77
|
+
// Sorted, non-overlapping, and — this is the part that matters — built out of
|
|
78
|
+
// PAIRS OF ITS OWN. A class is assembled by concatenating the members' range
|
|
79
|
+
// lists, and a positive shorthand contributes the shared `DIGIT` / `WORD` /
|
|
80
|
+
// `SPACE` table itself rather than a copy of it. Widening a pair in place to
|
|
81
|
+
// absorb a neighbour therefore edited that table for the whole process: after
|
|
82
|
+
// one `[\d:]`, every `\d` compiled anywhere in the program matched a colon, and
|
|
83
|
+
// the operator's other validators quietly widened with it.
|
|
84
|
+
function _norm(ranges) {
|
|
85
|
+
var sorted = ranges.map(function (r) { return [r[0], r[1]]; });
|
|
86
|
+
if (sorted.length < 2) return sorted;
|
|
87
|
+
sorted.sort(function (a, b) { return a[0] - b[0] || a[1] - b[1]; });
|
|
88
|
+
var out = [sorted[0]];
|
|
89
|
+
for (var i = 1; i < sorted.length; i += 1) {
|
|
90
|
+
var last = out[out.length - 1];
|
|
91
|
+
var next = sorted[i];
|
|
92
|
+
if (next[0] <= last[1] + 1) {
|
|
93
|
+
if (next[1] > last[1]) last[1] = next[1];
|
|
94
|
+
} else out.push(next);
|
|
95
|
+
}
|
|
96
|
+
return out;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Deep-frozen so that a future write through a shared table fails loudly at the
|
|
100
|
+
// line that made it, rather than silently widening a shorthand everywhere.
|
|
101
|
+
function _sealed(ranges) {
|
|
102
|
+
ranges.forEach(function (r) { Object.freeze(r); });
|
|
103
|
+
return Object.freeze(ranges);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function _one(cp) { return [[cp, cp]]; }
|
|
107
|
+
|
|
108
|
+
function _union(a, b) { return _norm(a.concat(b)); }
|
|
109
|
+
|
|
110
|
+
function _negate(ranges) {
|
|
111
|
+
var sorted = _norm(ranges);
|
|
112
|
+
var out = [];
|
|
113
|
+
var at = 0;
|
|
114
|
+
for (var i = 0; i < sorted.length; i += 1) {
|
|
115
|
+
if (sorted[i][0] > at) out.push([at, sorted[i][0] - 1]);
|
|
116
|
+
at = Math.max(at, sorted[i][1] + 1);
|
|
117
|
+
}
|
|
118
|
+
if (at <= MAX_CODE_POINT) out.push([at, MAX_CODE_POINT]);
|
|
119
|
+
return out;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Everything that shares a case with a member of these ranges. A set that is
|
|
123
|
+
// COMPLEMENTED under `i` has to be complemented on its case closure rather than
|
|
124
|
+
// on itself: under `iu` a Kelvin sign folds to `k`, so it is a word character
|
|
125
|
+
// and belongs outside `\W`. Complementing the bare list put it in `\W` as well,
|
|
126
|
+
// and then the fold test found `k` there and `[\W]` matched a letter.
|
|
127
|
+
//
|
|
128
|
+
// Only the shorthand sets are ever complemented, so this walks a hundred or so
|
|
129
|
+
// code points; it is not a general-purpose closure over an arbitrary class.
|
|
130
|
+
function _caseClosure(ranges, icase, unicode) {
|
|
131
|
+
if (!icase) return ranges;
|
|
132
|
+
var out = ranges.slice();
|
|
133
|
+
for (var i = 0; i < ranges.length; i += 1) {
|
|
134
|
+
for (var cp = ranges[i][0]; cp <= ranges[i][1]; cp += 1) {
|
|
135
|
+
var partners = codepointClass.caseFoldPartners(cp, unicode);
|
|
136
|
+
for (var p = 0; p < partners.length; p += 1) {
|
|
137
|
+
out.push([partners[p], partners[p]]);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return _norm(out);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Does the pattern declare a named group anywhere in it? Read with an eye to
|
|
145
|
+
// where a `(` does NOT open a group: behind a backslash, or inside a class,
|
|
146
|
+
// where `(?<` is four ordinary members. A lookbehind head — `(?<=` or `(?<!` —
|
|
147
|
+
// names nothing either.
|
|
148
|
+
function _declaresNamedGroup(text) {
|
|
149
|
+
var inClass = false;
|
|
150
|
+
for (var i = 0; i < text.length; i += 1) {
|
|
151
|
+
var ch = text.charAt(i);
|
|
152
|
+
if (ch === "\\") { i += 1; continue; }
|
|
153
|
+
if (inClass) { if (ch === "]") inClass = false; continue; }
|
|
154
|
+
if (ch === "[") { inClass = true; continue; }
|
|
155
|
+
if (ch === "(" && text.charAt(i + 1) === "?" && text.charAt(i + 2) === "<" &&
|
|
156
|
+
text.charAt(i + 3) !== "=" && text.charAt(i + 3) !== "!") {
|
|
157
|
+
return true;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function _has(ranges, cp) {
|
|
164
|
+
var lo = 0;
|
|
165
|
+
var hi = ranges.length - 1;
|
|
166
|
+
while (lo <= hi) {
|
|
167
|
+
var mid = (lo + hi) >> 1;
|
|
168
|
+
if (cp < ranges[mid][0]) hi = mid - 1;
|
|
169
|
+
else if (cp > ranges[mid][1]) lo = mid + 1;
|
|
170
|
+
else return true;
|
|
171
|
+
}
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
var DIGIT = _sealed([[0x30, 0x39]]);
|
|
176
|
+
var WORD = _sealed([[0x30, 0x39], [0x41, 0x5A], [0x5F, 0x5F], [0x61, 0x7A]]);
|
|
177
|
+
var SPACE = _sealed(_norm([
|
|
178
|
+
[0x09, 0x0D], [0x20, 0x20], [0xA0, 0xA0], [0x1680, 0x1680], [0x2000, 0x200A],
|
|
179
|
+
[0x2028, 0x2029], [0x202F, 0x202F], [0x205F, 0x205F], [0x3000, 0x3000],
|
|
180
|
+
[0xFEFF, 0xFEFF],
|
|
181
|
+
]));
|
|
182
|
+
var LINE_TERMINATORS = _sealed([[0x0A, 0x0A], [0x0D, 0x0D], [0x2028, 0x2029]]);
|
|
183
|
+
|
|
184
|
+
var CONTROL_ESCAPES = {
|
|
185
|
+
n: 0x0A, r: 0x0D, t: 0x09, f: 0x0C, v: 0x0B, 0: 0x00,
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
// ---- parsing ----------------------------------------------------------------
|
|
189
|
+
//
|
|
190
|
+
// Its own parser, for the reason above: this one has to be exact where the
|
|
191
|
+
// screen's may approximate, and it has to refuse rather than widen.
|
|
192
|
+
|
|
193
|
+
function _fail(message, code) {
|
|
194
|
+
throw new RegexLinearError(code, "regexLinear.compile: " + message);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Scanning helpers, spelled out. A pattern is read one character code at a
|
|
198
|
+
// time — not matched against patterns of its own, and never by slicing the
|
|
199
|
+
// source at every position, which would cost more than reading it.
|
|
200
|
+
|
|
201
|
+
function _isDigit(code) { return code >= 0x30 && code <= 0x39; }
|
|
202
|
+
|
|
203
|
+
function _hexDigit(code) {
|
|
204
|
+
if (code >= 0x30 && code <= 0x39) return code - 0x30;
|
|
205
|
+
if (code >= 0x41 && code <= 0x46) return code - 0x37;
|
|
206
|
+
if (code >= 0x61 && code <= 0x66) return code - 0x57;
|
|
207
|
+
return -1;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// `min`..`max` hex digits from `at`, or null.
|
|
211
|
+
function _readHex(src, at, min, max) {
|
|
212
|
+
var value = 0;
|
|
213
|
+
var count = 0;
|
|
214
|
+
while (count < max && at + count < src.length) {
|
|
215
|
+
var digit = _hexDigit(src.charCodeAt(at + count));
|
|
216
|
+
if (digit < 0) break;
|
|
217
|
+
value = value * 16 + digit;
|
|
218
|
+
count += 1;
|
|
219
|
+
}
|
|
220
|
+
if (count < min) return null;
|
|
221
|
+
return { value: value, end: at + count };
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// A repetition count. Anything with more digits than a real count could have is
|
|
225
|
+
// answered as one past what the compiler will expand, so it is refused there
|
|
226
|
+
// with the message about program size rather than silently becoming Infinity.
|
|
227
|
+
function _readCount(src, from, to) {
|
|
228
|
+
if (to - from > 9) return MAX_REPEAT_EXPANSION + 1;
|
|
229
|
+
var value = 0;
|
|
230
|
+
for (var at = from; at < to; at += 1) value = value * 10 + (src.charCodeAt(at) - 0x30);
|
|
231
|
+
return value;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// The characters a capture name is SPELLED WITH, with the identifier escapes
|
|
235
|
+
// the language allows resolved: a name written as an escape for the letter `a`
|
|
236
|
+
// is the name `a`, and `groups` carries it under that key. Returns null where
|
|
237
|
+
// the spelling is not one the language would read, so the caller can refuse it
|
|
238
|
+
// by the same route as any other bad name.
|
|
239
|
+
function _decodeCaptureName(spelling) {
|
|
240
|
+
var out = "";
|
|
241
|
+
var i = 0;
|
|
242
|
+
while (i < spelling.length) {
|
|
243
|
+
var ch = spelling.charAt(i);
|
|
244
|
+
if (ch !== "\\") { out += ch; i += 1; continue; }
|
|
245
|
+
if (spelling.charAt(i + 1) !== "u") return null; // only `\u` names a character
|
|
246
|
+
if (spelling.charAt(i + 2) === "{") {
|
|
247
|
+
var braced = _readHex(spelling, i + 3, 1, 6);
|
|
248
|
+
if (braced === null || spelling.charAt(braced.end) !== "}") return null;
|
|
249
|
+
if (braced.value > MAX_CODE_POINT) return null;
|
|
250
|
+
out += String.fromCodePoint(braced.value);
|
|
251
|
+
i = braced.end + 1;
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
var four = _readHex(spelling, i + 2, 4, 4);
|
|
255
|
+
if (four === null) return null;
|
|
256
|
+
out += String.fromCharCode(four.value);
|
|
257
|
+
i = four.end;
|
|
258
|
+
}
|
|
259
|
+
return out;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// A capture name is an identifier: it does not start with a digit and carries
|
|
263
|
+
// no punctuation. `(?<1>a)` and `(?<->a)` are patterns the platform refuses, so
|
|
264
|
+
// they are refused here rather than quietly given a meaning of their own.
|
|
265
|
+
function _isCaptureName(name) {
|
|
266
|
+
if (name.length === 0) return false;
|
|
267
|
+
for (var i = 0; i < name.length; i += 1) {
|
|
268
|
+
var code = name.charCodeAt(i);
|
|
269
|
+
var isLetter = (code >= 0x41 && code <= 0x5A) || (code >= 0x61 && code <= 0x7A);
|
|
270
|
+
var isDigit = code >= 0x30 && code <= 0x39;
|
|
271
|
+
var isNameSign = code === 0x24 || code === 0x5F; // `$` and `_`
|
|
272
|
+
// Deliberately ASCII. Which characters beyond it may start or continue an
|
|
273
|
+
// identifier is a Unicode property, and admitting everything above ASCII
|
|
274
|
+
// would take names the platform refuses — `(?<{co}py>a)` is not a name. A
|
|
275
|
+
// name outside ASCII is refused with a message saying so, rather than given
|
|
276
|
+
// a meaning here that `RegExp` would not give it.
|
|
277
|
+
if (isLetter || isNameSign) continue;
|
|
278
|
+
if (isDigit && i > 0) continue;
|
|
279
|
+
return false;
|
|
280
|
+
}
|
|
281
|
+
return true;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// The same rules `RegExp` applies: known letters, each at most once, and never
|
|
285
|
+
// both Unicode modes. A flag string this refuses is one the platform refuses.
|
|
286
|
+
function _badFlags(flags) {
|
|
287
|
+
var seen = "";
|
|
288
|
+
for (var i = 0; i < flags.length; i += 1) {
|
|
289
|
+
var ch = flags.charAt(i);
|
|
290
|
+
if ("dgimsuvy".indexOf(ch) === -1) return "unknown flag " + JSON.stringify(ch);
|
|
291
|
+
if (seen.indexOf(ch) !== -1) return "repeated flag " + JSON.stringify(ch);
|
|
292
|
+
seen += ch;
|
|
293
|
+
}
|
|
294
|
+
if (seen.indexOf("u") !== -1 && seen.indexOf("v") !== -1) {
|
|
295
|
+
return "the u and v flags cannot both be set";
|
|
296
|
+
}
|
|
297
|
+
return null;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function _parse(src, flags) {
|
|
301
|
+
var pos = 0;
|
|
302
|
+
var captureCount = 0;
|
|
303
|
+
// Without a prototype, because a group may legitimately be named
|
|
304
|
+
// `__proto__` and assigning that to an ordinary object sets the prototype
|
|
305
|
+
// instead of recording the name.
|
|
306
|
+
var names = Object.create(null);
|
|
307
|
+
var unicode = flags.indexOf("u") !== -1 || flags.indexOf("v") !== -1;
|
|
308
|
+
var icase = flags.indexOf("i") !== -1;
|
|
309
|
+
// Answered once, from the whole source, because it is a property of the
|
|
310
|
+
// pattern rather than of the position being read. Asked at each `\k` instead,
|
|
311
|
+
// a source carrying many of them would be re-scanned once per escape, and a
|
|
312
|
+
// matcher that promises linear MATCHING would have taken time quadratic in
|
|
313
|
+
// the pattern to compile.
|
|
314
|
+
var namesAGroup = _declaresNamedGroup(src);
|
|
315
|
+
|
|
316
|
+
function peek() { return src.charAt(pos); }
|
|
317
|
+
function eat(ch) { if (src.charAt(pos) === ch) { pos += 1; return true; } return false; }
|
|
318
|
+
|
|
319
|
+
function parseAlt() {
|
|
320
|
+
var branches = [parseSeq()];
|
|
321
|
+
while (eat("|")) branches.push(parseSeq());
|
|
322
|
+
return { t: "alt", bs: branches };
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function parseSeq() {
|
|
326
|
+
var terms = [];
|
|
327
|
+
for (;;) {
|
|
328
|
+
var ch = peek();
|
|
329
|
+
if (ch === "" || ch === "|" || ch === ")") break;
|
|
330
|
+
var capLo = captureCount;
|
|
331
|
+
var atom = parseAtom(); // refuses rather than returning nothing
|
|
332
|
+
var capHi = captureCount;
|
|
333
|
+
var quant = parseQuantifier();
|
|
334
|
+
if (quant.min > quant.max) {
|
|
335
|
+
_fail("numbers out of order in {} quantifier", "regex/bad-quantifier");
|
|
336
|
+
}
|
|
337
|
+
// Nothing repeats an assertion. `^{1}a` and `\b{0}a` are syntax errors in
|
|
338
|
+
// JavaScript, and reading them as "the assertion, once" or "the assertion,
|
|
339
|
+
// never" would give an invalid pattern a meaning of its own — the second
|
|
340
|
+
// one silently dropping the boundary the operator asked for.
|
|
341
|
+
if (quant.present && (atom.t === "bol" || atom.t === "eol" ||
|
|
342
|
+
atom.t === "wb" || atom.t === "nwb")) {
|
|
343
|
+
_fail("nothing to repeat — a quantifier cannot be applied to an assertion",
|
|
344
|
+
"regex/bad-quantifier");
|
|
345
|
+
}
|
|
346
|
+
terms.push({
|
|
347
|
+
n: atom, min: quant.min, max: quant.max, lazy: quant.lazy,
|
|
348
|
+
capLo: capLo, capHi: capHi,
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
return { t: "seq", xs: terms };
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// `present` says whether a quantifier was WRITTEN, which is not the same as
|
|
355
|
+
// whether it changes anything: `^{1}` is a syntax error even though repeating
|
|
356
|
+
// once would have meant nothing.
|
|
357
|
+
function parseQuantifier() {
|
|
358
|
+
var plain = { min: 1, max: 1, lazy: false, present: false };
|
|
359
|
+
var ch = peek();
|
|
360
|
+
var min, max;
|
|
361
|
+
if (ch === "*") { pos += 1; min = 0; max = Infinity; }
|
|
362
|
+
else if (ch === "+") { pos += 1; min = 1; max = Infinity; }
|
|
363
|
+
else if (ch === "?") { pos += 1; min = 0; max = 1; }
|
|
364
|
+
else if (ch === "{") {
|
|
365
|
+
var scanned = scanBraceQuantifier(pos);
|
|
366
|
+
if (scanned === null) return plain; // a literal `{`
|
|
367
|
+
pos = scanned.end;
|
|
368
|
+
min = scanned.min;
|
|
369
|
+
max = scanned.max;
|
|
370
|
+
} else return plain;
|
|
371
|
+
var lazy = eat("?");
|
|
372
|
+
// `a++` is a syntax error in JavaScript. Swallowing it as a possessive
|
|
373
|
+
// quantifier would accept a pattern the platform refuses, and quietly.
|
|
374
|
+
if (!lazy && peek() === "+") _fail("nothing to repeat", "regex/bad-quantifier");
|
|
375
|
+
return { min: min, max: max, lazy: lazy, present: true };
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// `{n}` / `{n,}` / `{n,m}`, read one character at a time.
|
|
379
|
+
function scanBraceQuantifier(from) {
|
|
380
|
+
var at = from + 1; // past the `{`
|
|
381
|
+
var loStart = at;
|
|
382
|
+
while (at < src.length && _isDigit(src.charCodeAt(at))) at += 1;
|
|
383
|
+
if (at === loStart) return null;
|
|
384
|
+
var lo = _readCount(src, loStart, at);
|
|
385
|
+
var hi = lo;
|
|
386
|
+
if (src.charAt(at) === ",") {
|
|
387
|
+
at += 1;
|
|
388
|
+
var hiStart = at;
|
|
389
|
+
while (at < src.length && _isDigit(src.charCodeAt(at))) at += 1;
|
|
390
|
+
hi = at === hiStart ? Infinity : _readCount(src, hiStart, at);
|
|
391
|
+
}
|
|
392
|
+
if (src.charAt(at) !== "}") return null;
|
|
393
|
+
return { min: lo, max: hi, end: at + 1 };
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
function parseAtom() {
|
|
397
|
+
var ch = peek();
|
|
398
|
+
if (ch === "(") return parseGroup();
|
|
399
|
+
if (ch === "[") return parseClass();
|
|
400
|
+
if (ch === "^") { pos += 1; return { t: "bol" }; }
|
|
401
|
+
if (ch === "$") { pos += 1; return { t: "eol" }; }
|
|
402
|
+
if (ch === ".") {
|
|
403
|
+
pos += 1;
|
|
404
|
+
return flags.indexOf("s") !== -1
|
|
405
|
+
? { t: "set", r: [[0, MAX_CODE_POINT]], neg: false }
|
|
406
|
+
: { t: "set", r: LINE_TERMINATORS, neg: true };
|
|
407
|
+
}
|
|
408
|
+
if (ch === "\\") return parseEscape();
|
|
409
|
+
if (ch === "*" || ch === "+" || ch === "?") {
|
|
410
|
+
_fail("nothing to repeat", "regex/bad-quantifier");
|
|
411
|
+
}
|
|
412
|
+
// A COMPLETE `{n}` / `{n,}` / `{n,m}` standing where an atom belongs is a
|
|
413
|
+
// syntax error rather than literal text — `{1}` and `a{0}{1}` are both
|
|
414
|
+
// refused by the platform. Reading them as the characters they are spelled
|
|
415
|
+
// with would give a pattern nobody can run a private meaning here. An
|
|
416
|
+
// incomplete brace — `{1`, `{a}` — really is literal, and stays so.
|
|
417
|
+
if (ch === "{" && scanBraceQuantifier(pos) !== null) {
|
|
418
|
+
_fail("nothing to repeat", "regex/bad-quantifier");
|
|
419
|
+
}
|
|
420
|
+
refuseLoneSyntaxChar(ch);
|
|
421
|
+
pos += 1;
|
|
422
|
+
if (unicode) {
|
|
423
|
+
var cp = src.codePointAt(pos - 1);
|
|
424
|
+
if (cp > 0xFFFF) pos += 1;
|
|
425
|
+
return { t: "set", r: _one(cp), neg: false };
|
|
426
|
+
}
|
|
427
|
+
return { t: "set", r: _one(ch.charCodeAt(0)), neg: false };
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// Under `u` a bare `{`, `}` or `]` outside a class is a syntax error rather
|
|
431
|
+
// than a literal. Reading them as literals would take a pattern the platform
|
|
432
|
+
// refuses and give it a meaning of its own.
|
|
433
|
+
function refuseLoneSyntaxChar(ch) {
|
|
434
|
+
if (!unicode) return;
|
|
435
|
+
if (ch === "{" || ch === "}" || ch === "]") {
|
|
436
|
+
_fail("lone " + JSON.stringify(ch) + " is a syntax error under the u flag — " +
|
|
437
|
+
"escape it as \\" + ch, "regex/bad-escape");
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
function parseGroup() {
|
|
442
|
+
pos += 1; // the `(`
|
|
443
|
+
var capture = null;
|
|
444
|
+
var name = null;
|
|
445
|
+
if (peek() === "?") {
|
|
446
|
+
var rest = src.slice(pos, pos + 3);
|
|
447
|
+
if (rest.charAt(1) === "=" || rest.charAt(1) === "!" ||
|
|
448
|
+
(rest.charAt(1) === "<" && (rest.charAt(2) === "=" || rest.charAt(2) === "!"))) {
|
|
449
|
+
_fail("lookaround cannot be run in linear time — screen it with " +
|
|
450
|
+
"b.guardRegex.assertSafe and run it with the platform engine, or " +
|
|
451
|
+
"rewrite the pattern without it", "regex/unsupported-lookaround");
|
|
452
|
+
}
|
|
453
|
+
if (rest.charAt(1) === "<") { // named group
|
|
454
|
+
pos += 2;
|
|
455
|
+
var end = src.indexOf(">", pos);
|
|
456
|
+
if (end === -1) _fail("invalid capture group name", "regex/bad-group");
|
|
457
|
+
var spelling = src.slice(pos, end);
|
|
458
|
+
// A name may spell its characters with the identifier escapes the
|
|
459
|
+
// language allows, so it is decoded before it is judged: a group whose
|
|
460
|
+
// name is written as an escape for the letter `a` IS named `a`. Reading
|
|
461
|
+
// the source spelling refused a name the platform accepts, and would
|
|
462
|
+
// have recorded the escape itself as the key in `groups`.
|
|
463
|
+
name = _decodeCaptureName(spelling);
|
|
464
|
+
if (name === null || !_isCaptureName(name)) {
|
|
465
|
+
_fail("capture group name " + JSON.stringify(spelling) + " — a name here is " +
|
|
466
|
+
"ASCII letters, digits, `$` and `_`, not starting with a digit. " +
|
|
467
|
+
"Which characters beyond ASCII may name a group is a Unicode " +
|
|
468
|
+
"property this does not carry, so such a name is refused rather " +
|
|
469
|
+
"than given a meaning the platform would not give it",
|
|
470
|
+
"regex/bad-group");
|
|
471
|
+
}
|
|
472
|
+
pos = end + 1;
|
|
473
|
+
captureCount += 1;
|
|
474
|
+
capture = captureCount;
|
|
475
|
+
if (Object.prototype.hasOwnProperty.call(names, name)) {
|
|
476
|
+
_fail("duplicate capture group name " + JSON.stringify(name), "regex/bad-group");
|
|
477
|
+
}
|
|
478
|
+
names[name] = capture;
|
|
479
|
+
} else if (rest.charAt(1) === ":") {
|
|
480
|
+
pos += 2;
|
|
481
|
+
} else {
|
|
482
|
+
_fail("unsupported group " + JSON.stringify(src.slice(pos - 1, pos + 3)),
|
|
483
|
+
"regex/unsupported-group");
|
|
484
|
+
}
|
|
485
|
+
} else {
|
|
486
|
+
captureCount += 1;
|
|
487
|
+
capture = captureCount;
|
|
488
|
+
}
|
|
489
|
+
var body = parseAlt();
|
|
490
|
+
if (!eat(")")) _fail("unterminated group", "regex/bad-group");
|
|
491
|
+
return { t: "grp", b: body, cap: capture, name: name };
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// A shorthand outside a class, or a single escaped character.
|
|
495
|
+
function parseEscape() {
|
|
496
|
+
pos += 1; // the backslash
|
|
497
|
+
var ch = peek();
|
|
498
|
+
if (ch === "") _fail("trailing backslash", "regex/bad-escape");
|
|
499
|
+
if (ch === "b") { pos += 1; return { t: "wb" }; }
|
|
500
|
+
if (ch === "B") { pos += 1; return { t: "nwb" }; }
|
|
501
|
+
if (ch >= "1" && ch <= "9") {
|
|
502
|
+
// `\1` is a backreference where a first group exists and a legacy octal
|
|
503
|
+
// escape where none does — a distinction that cannot be made until the
|
|
504
|
+
// whole pattern has been read, and one the operator can make at a glance.
|
|
505
|
+
// A backreference cannot be simulated in linear time, so both are refused
|
|
506
|
+
// and the message names the escape that says what was meant.
|
|
507
|
+
_fail("a digit escape is a backreference, which cannot be run in linear " +
|
|
508
|
+
"time — screen it with b.guardRegex.assertSafe and run it with the " +
|
|
509
|
+
"platform engine, or rewrite it without the backreference. If a " +
|
|
510
|
+
"character was meant, write it as \\xNN or \\uNNNN rather than as a " +
|
|
511
|
+
"legacy octal escape", "regex/unsupported-backreference");
|
|
512
|
+
}
|
|
513
|
+
// `\k` names a group where the pattern declares one and is an identity
|
|
514
|
+
// escape where it declares none — `/\k/` matches a `k`. The group may be
|
|
515
|
+
// written after the `\k` that names it, so the answer comes from the whole
|
|
516
|
+
// pattern rather than from what has been read so far. Under `u` a `\k` that
|
|
517
|
+
// names nothing is a syntax error, which the identity path already reports.
|
|
518
|
+
if (ch === "k" && namesAGroup) {
|
|
519
|
+
_fail("a named backreference cannot be run in linear time — screen it " +
|
|
520
|
+
"with b.guardRegex.assertSafe and run it with the platform engine, " +
|
|
521
|
+
"or rewrite the pattern without it", "regex/unsupported-backreference");
|
|
522
|
+
}
|
|
523
|
+
var shorthand = _shorthand(ch);
|
|
524
|
+
if (shorthand !== null) { pos += 1; return { t: "set", r: shorthand.r, neg: shorthand.neg }; }
|
|
525
|
+
return { t: "set", r: _one(_escapedCodePoint(false)), neg: false };
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
function _shorthand(ch) {
|
|
529
|
+
if (ch === "d") return { r: DIGIT, neg: false };
|
|
530
|
+
if (ch === "D") return { r: DIGIT, neg: true };
|
|
531
|
+
if (ch === "w") return { r: WORD, neg: false };
|
|
532
|
+
if (ch === "W") return { r: WORD, neg: true };
|
|
533
|
+
if (ch === "s") return { r: SPACE, neg: false };
|
|
534
|
+
if (ch === "S") return { r: SPACE, neg: true };
|
|
535
|
+
return null;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
// The code point an escape stands for, having consumed it. `inClass` matters
|
|
539
|
+
// because the legacy grammar reads several escapes differently between the two
|
|
540
|
+
// places: `\c1` names a control inside a class and is a literal backslash
|
|
541
|
+
// outside one.
|
|
542
|
+
function _escapedCodePoint(inClass) {
|
|
543
|
+
var ch = src.charAt(pos);
|
|
544
|
+
// Outside Unicode mode a run of octal digits after a backslash is ONE
|
|
545
|
+
// character: `\07` is a bell, not a NUL followed by a seven. Reading only
|
|
546
|
+
// the `\0` left the digit behind as an atom of its own, which compiles
|
|
547
|
+
// happily and matches something else.
|
|
548
|
+
if (ch >= "0" && ch <= "7" && !unicode) {
|
|
549
|
+
var value = 0;
|
|
550
|
+
var digits = 0;
|
|
551
|
+
while (digits < 3 && pos < src.length) {
|
|
552
|
+
var digit = src.charCodeAt(pos) - 0x30;
|
|
553
|
+
if (digit < 0 || digit > 7) break;
|
|
554
|
+
if (value * 8 + digit > 0xFF) break; // three digits only up to \377
|
|
555
|
+
value = value * 8 + digit;
|
|
556
|
+
pos += 1;
|
|
557
|
+
digits += 1;
|
|
558
|
+
}
|
|
559
|
+
return value;
|
|
560
|
+
}
|
|
561
|
+
if (Object.prototype.hasOwnProperty.call(CONTROL_ESCAPES, ch)) {
|
|
562
|
+
// Under `u`, `\0` stands alone: `\01` and `\08` are invalid decimal
|
|
563
|
+
// escapes, not a NUL beside a digit.
|
|
564
|
+
if (ch === "0" && unicode && _isDigit(src.charCodeAt(pos + 1))) {
|
|
565
|
+
_fail("a digit cannot follow \\0 under the u flag", "regex/bad-escape");
|
|
566
|
+
}
|
|
567
|
+
pos += 1;
|
|
568
|
+
return CONTROL_ESCAPES[ch];
|
|
569
|
+
}
|
|
570
|
+
if (ch === "x") {
|
|
571
|
+
var hex = _readHex(src, pos + 1, 2, 2);
|
|
572
|
+
if (hex !== null) { pos = hex.end; return hex.value; }
|
|
573
|
+
// Without `u`, an `\x` that is not followed by two hex digits is an
|
|
574
|
+
// identity escape rather than an error: `\xZZ` matches an `x` and two
|
|
575
|
+
// `Z`s. It falls through to the identity path below.
|
|
576
|
+
if (unicode) _fail("invalid \\x escape", "regex/bad-escape");
|
|
577
|
+
}
|
|
578
|
+
if (ch === "u") {
|
|
579
|
+
// `\u{...}` is Unicode-mode syntax. Without `u` the platform reads it as
|
|
580
|
+
// a `u` followed by a brace, so accepting it here would match a different
|
|
581
|
+
// language than the same pattern handed to `RegExp`.
|
|
582
|
+
if (src.charAt(pos + 1) === "{" && unicode) {
|
|
583
|
+
var braced = _readHex(src, pos + 2, 1, 6);
|
|
584
|
+
if (braced === null || src.charAt(braced.end) !== "}") {
|
|
585
|
+
_fail("invalid \\u escape", "regex/bad-escape");
|
|
586
|
+
}
|
|
587
|
+
if (braced.value > MAX_CODE_POINT) _fail("invalid \\u escape", "regex/bad-escape");
|
|
588
|
+
pos = braced.end + 1;
|
|
589
|
+
return braced.value;
|
|
590
|
+
}
|
|
591
|
+
var four = _readHex(src, pos + 1, 4, 4);
|
|
592
|
+
if (four === null) {
|
|
593
|
+
// Outside Unicode mode a `\u` that is not followed by four hex digits
|
|
594
|
+
// is just an escaped `u`, and what comes after it is read on its own —
|
|
595
|
+
// `\u{61}` is a `u` repeated. Refusing it would turn away a pattern the
|
|
596
|
+
// platform accepts.
|
|
597
|
+
if (unicode) _fail("invalid \\u escape", "regex/bad-escape");
|
|
598
|
+
pos += 1;
|
|
599
|
+
return 0x75; // the letter `u`
|
|
600
|
+
}
|
|
601
|
+
pos = four.end;
|
|
602
|
+
// Under `u`, a surrogate pair spells ONE character. Left as two halves it
|
|
603
|
+
// would never match a subject that is read a code point at a time.
|
|
604
|
+
if (unicode && four.value >= 0xD800 && four.value <= 0xDBFF &&
|
|
605
|
+
src.charAt(pos) === "\\" && src.charAt(pos + 1) === "u") {
|
|
606
|
+
var low = _readHex(src, pos + 2, 4, 4);
|
|
607
|
+
if (low !== null && low.value >= 0xDC00 && low.value <= 0xDFFF) {
|
|
608
|
+
pos = low.end;
|
|
609
|
+
return (four.value - 0xD800) * 0x400 + (low.value - 0xDC00) + 0x10000;
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
return four.value;
|
|
613
|
+
}
|
|
614
|
+
if (ch === "c") {
|
|
615
|
+
var letter = src.charCodeAt(pos + 1);
|
|
616
|
+
var isLetter = (letter >= 0x41 && letter <= 0x5A) || (letter >= 0x61 && letter <= 0x7A);
|
|
617
|
+
// Inside a class and without `u`, a digit or an underscore names a control
|
|
618
|
+
// character as well: `[\c1]` is U+0011 and `[\c_]` is U+001F.
|
|
619
|
+
var isClassControl = !unicode && inClass &&
|
|
620
|
+
((letter >= 0x30 && letter <= 0x39) || letter === 0x5F);
|
|
621
|
+
if (isLetter || isClassControl) {
|
|
622
|
+
pos += 2;
|
|
623
|
+
return (letter & 0x1F); // the control it names
|
|
624
|
+
}
|
|
625
|
+
if (unicode) _fail("invalid \\c escape", "regex/bad-escape");
|
|
626
|
+
// Without `u`, a `\c` that names no control is a LITERAL BACKSLASH and the
|
|
627
|
+
// `c` behind it is read on its own — `/\c1/` matches the three characters
|
|
628
|
+
// `\c1`, and `[\c]` holds a backslash and a `c`. Leaving `pos` on the `c`
|
|
629
|
+
// is what hands it back to the caller as an ordinary character.
|
|
630
|
+
return 0x5C;
|
|
631
|
+
}
|
|
632
|
+
// A property escape is Unicode-mode syntax. Without `u` the platform reads
|
|
633
|
+
// `\p{L}` as a `p`, a brace, an `L` and a brace, so refusing it there turned
|
|
634
|
+
// away a pattern that runs perfectly well.
|
|
635
|
+
if ((ch === "p" || ch === "P") && unicode) {
|
|
636
|
+
_fail("a unicode property escape is not supported — name the characters " +
|
|
637
|
+
"in a class instead", "regex/unsupported-property");
|
|
638
|
+
}
|
|
639
|
+
// An identity escape. Outside Unicode mode the platform lets a backslash
|
|
640
|
+
// stand in front of anything; under `u` only the syntax characters may be
|
|
641
|
+
// escaped, and `\a` is a syntax error. Accepting it here would take a
|
|
642
|
+
// pattern the platform refuses.
|
|
643
|
+
// Inside a class the hyphen joins them, because that is where escaping one
|
|
644
|
+
// means something: `[A-Za-z0-9_\-]` says the hyphen is a member and not the
|
|
645
|
+
// start of a range. Outside a class there is no range to disambiguate and
|
|
646
|
+
// `\-` stays a syntax error under `u`.
|
|
647
|
+
var escapable = unicode && inClass ? "^$\\.*+?()[]{}|/-" : "^$\\.*+?()[]{}|/";
|
|
648
|
+
if (unicode && escapable.indexOf(ch) === -1) {
|
|
649
|
+
_fail("invalid escape \\" + ch + " under the u flag — only a syntax " +
|
|
650
|
+
"character may be escaped there", "regex/bad-escape");
|
|
651
|
+
}
|
|
652
|
+
pos += 1;
|
|
653
|
+
// Outside Unicode mode the subject is read a UTF-16 unit at a time, so an
|
|
654
|
+
// escaped astral character is TWO units and has to compile as two. Taking
|
|
655
|
+
// the whole code point here would build a matcher that never matches it.
|
|
656
|
+
if (!unicode) return src.charCodeAt(pos - 1);
|
|
657
|
+
var cp = src.codePointAt(pos - 1);
|
|
658
|
+
if (cp > 0xFFFF) pos += 1;
|
|
659
|
+
return cp;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
function parseClass() {
|
|
663
|
+
pos += 1; // the `[`
|
|
664
|
+
var negated = eat("^");
|
|
665
|
+
var ranges = [];
|
|
666
|
+
var closed = false;
|
|
667
|
+
while (pos < src.length) {
|
|
668
|
+
if (eat("]")) { closed = true; break; }
|
|
669
|
+
var lo = classMember();
|
|
670
|
+
// A shorthand is a set, not a bound: `[\d-x]` is the digits, a hyphen
|
|
671
|
+
// and an `x`, and treating it as a range start would be a different class.
|
|
672
|
+
if (lo.set !== null) {
|
|
673
|
+
if (peek() === "-" && src.charAt(pos + 1) !== "]" && pos + 1 < src.length) {
|
|
674
|
+
// `[\d-x]` is a syntax error under `u` for the same reason `[a-\d]` is:
|
|
675
|
+
// a shorthand cannot stand at either end of a range.
|
|
676
|
+
if (unicode) {
|
|
677
|
+
_fail("a shorthand cannot be an end of a range under the u flag",
|
|
678
|
+
"regex/bad-class");
|
|
679
|
+
}
|
|
680
|
+
// Without `u` those are three members — the shorthand, a hyphen, and
|
|
681
|
+
// whatever followed the hyphen — and all three are taken HERE. Left
|
|
682
|
+
// for the next turn of the loop, the tail of `[\d-a-z]` reads as an
|
|
683
|
+
// `a`-to-`z` range and the class matches every letter between them;
|
|
684
|
+
// the platform takes only the `a` and the `z`.
|
|
685
|
+
pos += 1; // the `-`
|
|
686
|
+
var tail = classMember();
|
|
687
|
+
ranges = ranges.concat(lo.set);
|
|
688
|
+
ranges.push([0x2D, 0x2D]);
|
|
689
|
+
if (tail.set !== null) ranges = ranges.concat(tail.set);
|
|
690
|
+
else ranges.push([tail.cp, tail.cp]);
|
|
691
|
+
continue;
|
|
692
|
+
}
|
|
693
|
+
ranges = ranges.concat(lo.set);
|
|
694
|
+
continue;
|
|
695
|
+
}
|
|
696
|
+
if (peek() === "-" && src.charAt(pos + 1) !== "]" && pos + 1 < src.length) {
|
|
697
|
+
pos += 1;
|
|
698
|
+
var hi = classMember();
|
|
699
|
+
if (hi.set !== null) { // `[a-\d]` — not a range
|
|
700
|
+
// Outside Unicode mode that is three members: an `a`, a hyphen and
|
|
701
|
+
// the digits. Under `u` it is a syntax error, and reading it as the
|
|
702
|
+
// three would take a pattern the platform refuses.
|
|
703
|
+
if (unicode) {
|
|
704
|
+
_fail("a shorthand cannot be the end of a range under the u flag",
|
|
705
|
+
"regex/bad-class");
|
|
706
|
+
}
|
|
707
|
+
ranges.push([lo.cp, lo.cp]);
|
|
708
|
+
ranges.push([0x2D, 0x2D]);
|
|
709
|
+
ranges = ranges.concat(hi.set);
|
|
710
|
+
continue;
|
|
711
|
+
}
|
|
712
|
+
if (hi.cp < lo.cp) _fail("range out of order in character class", "regex/bad-class");
|
|
713
|
+
ranges.push([lo.cp, hi.cp]);
|
|
714
|
+
continue;
|
|
715
|
+
}
|
|
716
|
+
ranges.push([lo.cp, lo.cp]);
|
|
717
|
+
}
|
|
718
|
+
if (!closed) _fail("unterminated character class", "regex/bad-class");
|
|
719
|
+
// The `^` is carried rather than applied. Under `i` a class matches when
|
|
720
|
+
// any MEMBER shares a case with the character, and a negated class matches
|
|
721
|
+
// when none does — so the negation has to come after that test, not before
|
|
722
|
+
// it. Pre-negating let `[^abc]` match a `b`, because `B` was in the
|
|
723
|
+
// complement.
|
|
724
|
+
return { t: "set", r: _norm(ranges), neg: negated };
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
function classMember() {
|
|
728
|
+
if (peek() === "\\") {
|
|
729
|
+
pos += 1;
|
|
730
|
+
var ch = peek();
|
|
731
|
+
if (ch === "") _fail("trailing backslash", "regex/bad-escape");
|
|
732
|
+
// `\b` is a backspace inside a class, not a boundary.
|
|
733
|
+
if (ch === "b") { pos += 1; return { cp: 0x08, set: null }; }
|
|
734
|
+
// Once a pattern names a group, `\k` stops being an ordinary character
|
|
735
|
+
// ANYWHERE in it — including inside a class, where the grammar has no
|
|
736
|
+
// production for it at all, so `/(?<n>a)[\k]/` is a syntax error. Without
|
|
737
|
+
// a named group the same class holds a `k`.
|
|
738
|
+
if (ch === "k" && namesAGroup) {
|
|
739
|
+
_fail("invalid escape \\k inside a character class — a pattern that " +
|
|
740
|
+
"names a group cannot also use \\k as an ordinary character",
|
|
741
|
+
"regex/bad-escape");
|
|
742
|
+
}
|
|
743
|
+
var shorthand = _shorthand(ch);
|
|
744
|
+
if (shorthand !== null) {
|
|
745
|
+
pos += 1;
|
|
746
|
+
return {
|
|
747
|
+
cp: -1,
|
|
748
|
+
set: shorthand.neg
|
|
749
|
+
? _negate(_caseClosure(shorthand.r, icase, unicode))
|
|
750
|
+
: shorthand.r,
|
|
751
|
+
};
|
|
752
|
+
}
|
|
753
|
+
return { cp: _escapedCodePoint(true), set: null };
|
|
754
|
+
}
|
|
755
|
+
// Inside a class as outside it: without `u` an astral character is its two
|
|
756
|
+
// surrogate units, and reading the whole code point while advancing one
|
|
757
|
+
// unit records a character the subject never presents.
|
|
758
|
+
if (!unicode) {
|
|
759
|
+
pos += 1;
|
|
760
|
+
return { cp: src.charCodeAt(pos - 1), set: null };
|
|
761
|
+
}
|
|
762
|
+
var cp = src.codePointAt(pos);
|
|
763
|
+
pos += cp > 0xFFFF ? 2 : 1;
|
|
764
|
+
return { cp: cp, set: null };
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
var ast = parseAlt();
|
|
768
|
+
if (pos < src.length) {
|
|
769
|
+
_fail("unmatched " + JSON.stringify(src.charAt(pos)), "regex/unbalanced");
|
|
770
|
+
}
|
|
771
|
+
return { ast: ast, captureCount: captureCount, names: names };
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
// ---- compiling to a program -------------------------------------------------
|
|
775
|
+
//
|
|
776
|
+
// Instructions: `c` consumes a character, `s` splits into two paths in priority
|
|
777
|
+
// order, `j` jumps, `v` records a capture boundary, `a` tests a zero-width
|
|
778
|
+
// assertion, `m` matches. Nothing in this set can backtrack, because nothing in
|
|
779
|
+
// it ever un-does a step.
|
|
780
|
+
|
|
781
|
+
// Can this match without consuming anything?
|
|
782
|
+
function _isNullable(n) {
|
|
783
|
+
if (n.t === "set") return false;
|
|
784
|
+
if (n.t === "bol" || n.t === "eol" || n.t === "wb" || n.t === "nwb") return true;
|
|
785
|
+
if (n.t === "grp") return _isNullable(n.b);
|
|
786
|
+
if (n.t === "alt") {
|
|
787
|
+
for (var b = 0; b < n.bs.length; b += 1) if (_isNullable(n.bs[b])) return true;
|
|
788
|
+
return false;
|
|
789
|
+
}
|
|
790
|
+
for (var i = 0; i < n.xs.length; i += 1) {
|
|
791
|
+
var t = n.xs[i];
|
|
792
|
+
if (t.min > 0 && !_isNullable(t.n)) return false;
|
|
793
|
+
}
|
|
794
|
+
return true;
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
function _compile(ast, captureCount) {
|
|
798
|
+
var prog = [];
|
|
799
|
+
// The whole program, not one quantifier at a time. Counts multiply when they
|
|
800
|
+
// nest — `(a{4096}){4096}` passes any per-term cap and asks for sixteen
|
|
801
|
+
// million instructions — so what is bounded is the total, which is the thing
|
|
802
|
+
// that actually costs memory.
|
|
803
|
+
function emit(instruction) {
|
|
804
|
+
if (prog.length >= MAX_PROGRAM_LENGTH) {
|
|
805
|
+
_fail("the pattern expands to more than " + MAX_PROGRAM_LENGTH + " steps — " +
|
|
806
|
+
"repetition counts multiply where they nest, so a short pattern can " +
|
|
807
|
+
"ask for a very large program", "regex/repeat-too-large");
|
|
808
|
+
}
|
|
809
|
+
prog.push(instruction);
|
|
810
|
+
return prog.length - 1;
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
function node(n) {
|
|
814
|
+
if (n.t === "set") { emit({ op: "c", set: { r: n.r, neg: !!n.neg } }); return; }
|
|
815
|
+
if (n.t === "bol" || n.t === "eol" || n.t === "wb" || n.t === "nwb") {
|
|
816
|
+
emit({ op: "a", k: n.t });
|
|
817
|
+
return;
|
|
818
|
+
}
|
|
819
|
+
if (n.t === "grp") {
|
|
820
|
+
if (n.cap !== null) emit({ op: "v", i: n.cap * 2 });
|
|
821
|
+
alt(n.b);
|
|
822
|
+
if (n.cap !== null) emit({ op: "v", i: n.cap * 2 + 1 });
|
|
823
|
+
return;
|
|
824
|
+
}
|
|
825
|
+
if (n.t === "alt") { alt(n); return; }
|
|
826
|
+
if (n.t === "seq") { seq(n); return; }
|
|
827
|
+
_fail("unsupported construct", "regex/unsupported");
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
function seq(s) {
|
|
831
|
+
for (var i = 0; i < s.xs.length; i += 1) term(s.xs[i]);
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
// One pass through a repeated body. Each pass starts with the groups inside
|
|
835
|
+
// it forgotten: a group that took part in an earlier repetition and not in
|
|
836
|
+
// the last one reads as absent, which is what `(?:(a)|b)+` against "ab" says
|
|
837
|
+
// about its group. Carrying the earlier value forward would report a capture
|
|
838
|
+
// the match did not make.
|
|
839
|
+
function iteration(t) {
|
|
840
|
+
if ((t.min !== 1 || t.max !== 1) && t.capHi > t.capLo) {
|
|
841
|
+
emit({ op: "clr", lo: t.capLo + 1, hi: t.capHi });
|
|
842
|
+
}
|
|
843
|
+
node(t.n);
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
function alt(a) {
|
|
847
|
+
if (a.bs.length === 1) { seq(a.bs[0]); return; }
|
|
848
|
+
var jumps = [];
|
|
849
|
+
for (var i = 0; i < a.bs.length; i += 1) {
|
|
850
|
+
if (i === a.bs.length - 1) { seq(a.bs[i]); break; }
|
|
851
|
+
var split = emit({ op: "s", x: 0, y: 0 });
|
|
852
|
+
prog[split].x = prog.length;
|
|
853
|
+
seq(a.bs[i]);
|
|
854
|
+
jumps.push(emit({ op: "j", x: 0 }));
|
|
855
|
+
prog[split].y = prog.length;
|
|
856
|
+
}
|
|
857
|
+
for (var j = 0; j < jumps.length; j += 1) prog[jumps[j]].x = prog.length;
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
// A repeat is compiled by writing the body out: `n` mandatory copies, then
|
|
861
|
+
// the optional ones. That is what bounds the program by the counts the
|
|
862
|
+
// operator wrote, and why those counts are capped.
|
|
863
|
+
function term(t) {
|
|
864
|
+
var min = t.min;
|
|
865
|
+
var max = t.max;
|
|
866
|
+
if (max !== Infinity && max - min > MAX_REPEAT_EXPANSION) {
|
|
867
|
+
_fail("a repetition of more than " + MAX_REPEAT_EXPANSION + " is refused — " +
|
|
868
|
+
"the program would be larger than the subject", "regex/repeat-too-large");
|
|
869
|
+
}
|
|
870
|
+
if (min > MAX_REPEAT_EXPANSION) {
|
|
871
|
+
_fail("a repetition of more than " + MAX_REPEAT_EXPANSION + " is refused — " +
|
|
872
|
+
"the program would be larger than the subject", "regex/repeat-too-large");
|
|
873
|
+
}
|
|
874
|
+
// A repetition over a body that can match nothing is refused, and refused
|
|
875
|
+
// rather than approximated. Simulating every path at once is what makes
|
|
876
|
+
// this linear, and it works by keeping ONE live position per instruction —
|
|
877
|
+
// so two paths that reach the same instruction at the same place are the
|
|
878
|
+
// same path from here on. That is true of everything except an empty
|
|
879
|
+
// iteration, where whether the repetition may go round again depends on
|
|
880
|
+
// where its body began rather than on where it is now. Telling those paths
|
|
881
|
+
// apart means keeping a live position per instruction PER STARTING POINT,
|
|
882
|
+
// which is the quadratic behaviour this exists to avoid.
|
|
883
|
+
//
|
|
884
|
+
// Nothing is lost by saying so. `x*` where `x` can match nothing means
|
|
885
|
+
// exactly what `x` means, and the shapes this turns away — `(a*)*`,
|
|
886
|
+
// `(a*b*?)*` — are the ones that hang the platform engine.
|
|
887
|
+
if (max > min && _isNullable(t.n)) {
|
|
888
|
+
_fail("a repetition of something that can match nothing — rewrite the body " +
|
|
889
|
+
"so each repetition must consume, or drop the repetition, which " +
|
|
890
|
+
"means the same thing", "regex/nullable-repetition");
|
|
891
|
+
}
|
|
892
|
+
var i;
|
|
893
|
+
for (i = 0; i < min; i += 1) iteration(t);
|
|
894
|
+
if (max === Infinity) {
|
|
895
|
+
if (min === 0) star(t);
|
|
896
|
+
else plusTail(t); // the copies above were the `min`
|
|
897
|
+
return;
|
|
898
|
+
}
|
|
899
|
+
// Every optional copy skips to the END of the whole construct, not into the
|
|
900
|
+
// next copy. Landing in the next one matches the same language and gets the
|
|
901
|
+
// ORDER wrong — `x{0,3}` would try one `x` before three — and order is what
|
|
902
|
+
// decides which capture an operator sees.
|
|
903
|
+
var skips = [];
|
|
904
|
+
for (i = min; i < max; i += 1) {
|
|
905
|
+
var split = emit({ op: "s", x: 0, y: 0 });
|
|
906
|
+
var body = prog.length;
|
|
907
|
+
iteration(t);
|
|
908
|
+
skips.push(split);
|
|
909
|
+
if (t.lazy) prog[split].y = body; else prog[split].x = body;
|
|
910
|
+
}
|
|
911
|
+
var end = prog.length;
|
|
912
|
+
for (var k = 0; k < skips.length; k += 1) {
|
|
913
|
+
if (t.lazy) prog[skips[k]].x = end; else prog[skips[k]].y = end;
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
function star(t) {
|
|
918
|
+
var lazy = t.lazy;
|
|
919
|
+
var split = emit({ op: "s", x: 0, y: 0 });
|
|
920
|
+
var body = prog.length;
|
|
921
|
+
iteration(t);
|
|
922
|
+
emit({ op: "j", x: split });
|
|
923
|
+
var after = prog.length;
|
|
924
|
+
prog[split].x = lazy ? after : body;
|
|
925
|
+
prog[split].y = lazy ? body : after;
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
// `x+` after its mandatory copy: loop back over one more, or fall through.
|
|
929
|
+
function plusTail(t) {
|
|
930
|
+
var lazy = t.lazy;
|
|
931
|
+
var back = prog.length;
|
|
932
|
+
var split = emit({ op: "s", x: 0, y: 0 });
|
|
933
|
+
var body = prog.length;
|
|
934
|
+
iteration(t);
|
|
935
|
+
emit({ op: "j", x: back });
|
|
936
|
+
var after = prog.length;
|
|
937
|
+
prog[split].x = lazy ? after : body;
|
|
938
|
+
prog[split].y = lazy ? body : after;
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
emit({ op: "v", i: 0 });
|
|
942
|
+
node(ast);
|
|
943
|
+
emit({ op: "v", i: 1 });
|
|
944
|
+
emit({ op: "m" });
|
|
945
|
+
return { prog: prog, slots: (captureCount + 1) * 2 };
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
// ---- running ----------------------------------------------------------------
|
|
949
|
+
|
|
950
|
+
// A class matches when one of its MEMBERS is the character, or shares a case
|
|
951
|
+
// with it. Its `^` is applied to that answer, not to the members: `[^abc]`
|
|
952
|
+
// under `i` refuses `B` because `b` is a member, and negating the member list
|
|
953
|
+
// first would have said the opposite.
|
|
954
|
+
function _matchesSet(set, cp, icase, unicode) {
|
|
955
|
+
var member = _has(set.r, cp);
|
|
956
|
+
if (!member && icase) member = _sharesCase(set.r, cp, unicode);
|
|
957
|
+
return set.neg ? !member : member;
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
// Two characters are the same under `i` when they CANONICALIZE alike, which is
|
|
961
|
+
// not the same as one being the other's upper or lower case. The language folds
|
|
962
|
+
// through upper case, keeps a character whose upper case is more than one
|
|
963
|
+
// character, and — the rule that catches people out — refuses to fold a
|
|
964
|
+
// non-ASCII character onto an ASCII one. That last is why `/k/i` does not match
|
|
965
|
+
// a Kelvin sign, and why converting both ways and comparing gets it wrong.
|
|
966
|
+
//
|
|
967
|
+
// Under `u` the rule is different again: characters are folded rather than
|
|
968
|
+
// upper-cased, and the ASCII guard does not apply — which is why `/s/iu` DOES
|
|
969
|
+
// match a long s, and `/s/i` does not.
|
|
970
|
+
function _canonicalize(cp, unicode) {
|
|
971
|
+
return codepointClass.canonicalizeForCase(cp, unicode);
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
// Is some member of these ranges the same character as `cp` under `i`? The
|
|
975
|
+
// class is asked for in full rather than guessed at from `cp`'s own upper and
|
|
976
|
+
// lower forms: a final sigma is an ordinary sigma, and no amount of casing the
|
|
977
|
+
// ordinary one leads to it.
|
|
978
|
+
function _sharesCase(ranges, cp, unicode) {
|
|
979
|
+
var partners = codepointClass.caseFoldPartners(cp, unicode);
|
|
980
|
+
for (var i = 0; i < partners.length; i += 1) {
|
|
981
|
+
if (_has(ranges, partners[i])) return true;
|
|
982
|
+
}
|
|
983
|
+
return false;
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
function _run(compiled, subject, startAt, opts) {
|
|
987
|
+
var prog = compiled.prog;
|
|
988
|
+
var slots = compiled.slots;
|
|
989
|
+
var icase = opts.icase;
|
|
990
|
+
var multiline = opts.multiline;
|
|
991
|
+
var unicode = opts.unicode;
|
|
992
|
+
var sticky = opts.sticky;
|
|
993
|
+
var len = subject.length;
|
|
994
|
+
|
|
995
|
+
// One live position per instruction, which is what makes the whole thing
|
|
996
|
+
// linear: a path that arrives where another already is has nothing new to
|
|
997
|
+
// explore, so it is dropped rather than followed.
|
|
998
|
+
//
|
|
999
|
+
// Each list keeps its OWN record of what it has admitted. Sharing one between
|
|
1000
|
+
// the current step and the next lets a position already taken in this step
|
|
1001
|
+
// turn away the path that reaches it in the next, and the loop back over a
|
|
1002
|
+
// repetition is exactly such a path — `a*` came out empty for want of this.
|
|
1003
|
+
var marksC = { seen: new Array(prog.length), gen: 1 };
|
|
1004
|
+
var marksN = { seen: new Array(prog.length), gen: 1 };
|
|
1005
|
+
var clist = [];
|
|
1006
|
+
var nlist = [];
|
|
1007
|
+
var matched = null;
|
|
1008
|
+
|
|
1009
|
+
function codePointAt(at) {
|
|
1010
|
+
if (at >= len) return -1;
|
|
1011
|
+
if (!unicode) return subject.charCodeAt(at);
|
|
1012
|
+
return subject.codePointAt(at);
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
function widthAt(at) {
|
|
1016
|
+
if (!unicode) return 1;
|
|
1017
|
+
var cp = subject.codePointAt(at);
|
|
1018
|
+
return cp > 0xFFFF ? 2 : 1;
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
// A character that folds onto a word character IS one, for boundaries as much
|
|
1022
|
+
// as for `\w`: under `iu` a Kelvin sign folds to `k`, so it stands inside a
|
|
1023
|
+
// word rather than beside one.
|
|
1024
|
+
function isWordAt(cp) {
|
|
1025
|
+
if (cp < 0) return false;
|
|
1026
|
+
if (_has(WORD, cp)) return true;
|
|
1027
|
+
return icase && _sharesCase(WORD, cp, unicode);
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
function assertionHolds(kind, at) {
|
|
1031
|
+
var before = at > 0 ? subject.charCodeAt(at - 1) : -1;
|
|
1032
|
+
var here = at < len ? subject.charCodeAt(at) : -1;
|
|
1033
|
+
if (kind === "bol") {
|
|
1034
|
+
return at === 0 || (multiline && _has(LINE_TERMINATORS, before));
|
|
1035
|
+
}
|
|
1036
|
+
if (kind === "eol") {
|
|
1037
|
+
return at === len || (multiline && _has(LINE_TERMINATORS, here));
|
|
1038
|
+
}
|
|
1039
|
+
var wordBefore = at > 0 && isWordAt(before);
|
|
1040
|
+
var wordHere = at < len && isWordAt(here);
|
|
1041
|
+
if (kind === "wb") return wordBefore !== wordHere;
|
|
1042
|
+
return wordBefore === wordHere; // `\B`
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
// Where the capture boundaries recorded so far are kept: a chain, one link
|
|
1046
|
+
// per boundary, shared by every path that passed through it. Recording one
|
|
1047
|
+
// costs a link rather than a copy of every slot — with an array, a pattern of
|
|
1048
|
+
// N groups copied 2N slots at each of its 2N boundaries, so matching grew
|
|
1049
|
+
// with the SQUARE of the pattern and the promised bound held only for
|
|
1050
|
+
// patterns that were small anyway. The chain is read once, when something
|
|
1051
|
+
// matches.
|
|
1052
|
+
//
|
|
1053
|
+
// Following the zero-width steps to the characters they lead to, with an
|
|
1054
|
+
// explicit stack rather than the call stack. A pattern can chain thousands of
|
|
1055
|
+
// them — `(){2000}` is nothing but zero-width steps — and recursion would
|
|
1056
|
+
// overflow on a pattern the platform matches without trouble.
|
|
1057
|
+
//
|
|
1058
|
+
// The stack keeps priority: a split's preferred side is pushed LAST so it
|
|
1059
|
+
// comes off first, which is what makes greedy greedy.
|
|
1060
|
+
function add(list, marks, startPc, at, startCaps) {
|
|
1061
|
+
var stack = [{ pc: startPc, caps: startCaps }];
|
|
1062
|
+
while (stack.length !== 0) {
|
|
1063
|
+
var step = stack.pop();
|
|
1064
|
+
var pc = step.pc;
|
|
1065
|
+
if (marks.seen[pc] === marks.gen) continue;
|
|
1066
|
+
marks.seen[pc] = marks.gen;
|
|
1067
|
+
var caps = step.caps;
|
|
1068
|
+
var instruction = prog[pc];
|
|
1069
|
+
if (instruction.op === "j") { stack.push({ pc: instruction.x, caps: caps }); continue; }
|
|
1070
|
+
if (instruction.op === "s") {
|
|
1071
|
+
stack.push({ pc: instruction.y, caps: caps });
|
|
1072
|
+
stack.push({ pc: instruction.x, caps: caps });
|
|
1073
|
+
continue;
|
|
1074
|
+
}
|
|
1075
|
+
if (instruction.op === "v") {
|
|
1076
|
+
stack.push({ pc: pc + 1, caps: { slot: instruction.i, at: at, prev: caps } });
|
|
1077
|
+
continue;
|
|
1078
|
+
}
|
|
1079
|
+
if (instruction.op === "a") {
|
|
1080
|
+
if (assertionHolds(instruction.k, at)) stack.push({ pc: pc + 1, caps: caps });
|
|
1081
|
+
continue;
|
|
1082
|
+
}
|
|
1083
|
+
if (instruction.op === "clr") {
|
|
1084
|
+
stack.push({ pc: pc + 1, caps: { lo: instruction.lo, hi: instruction.hi, prev: caps } });
|
|
1085
|
+
continue;
|
|
1086
|
+
}
|
|
1087
|
+
list.push({ pc: pc, caps: caps });
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
var at = startAt;
|
|
1092
|
+
|
|
1093
|
+
for (;;) {
|
|
1094
|
+
// A new attempt from this position, at the lowest priority, so an earlier
|
|
1095
|
+
// start always wins — which is what "leftmost" means. Once something has
|
|
1096
|
+
// matched, no later start can improve on it.
|
|
1097
|
+
if (matched === null && (!sticky || at === startAt)) add(clist, marksC, 0, at, null);
|
|
1098
|
+
// An empty list means nothing survived FROM HERE — `\b` refusing at the
|
|
1099
|
+
// first position, say. The search still moves on, because a later position
|
|
1100
|
+
// may start an attempt that does survive. Two things make that pointless: a
|
|
1101
|
+
// match already in hand, since no later start could be further left, and the
|
|
1102
|
+
// `y` flag, which allows exactly one start — once its attempt has died there
|
|
1103
|
+
// is nothing left to seed and walking the rest of the subject would be pure
|
|
1104
|
+
// cost. A sticky pattern refused twenty million characters in about a second
|
|
1105
|
+
// before this, reading every one of them to reach an answer it already had.
|
|
1106
|
+
if (clist.length === 0 && (matched !== null || sticky)) break;
|
|
1107
|
+
|
|
1108
|
+
var cp = codePointAt(at);
|
|
1109
|
+
var width = at < len ? widthAt(at) : 0;
|
|
1110
|
+
for (var i = 0; i < clist.length; i += 1) {
|
|
1111
|
+
var thread = clist[i];
|
|
1112
|
+
var instruction = prog[thread.pc];
|
|
1113
|
+
if (instruction.op === "m") {
|
|
1114
|
+
matched = thread.caps;
|
|
1115
|
+
break; // lower-priority threads lose
|
|
1116
|
+
}
|
|
1117
|
+
if (instruction.op === "c" && cp >= 0 && _matchesSet(instruction.set, cp, icase, unicode)) {
|
|
1118
|
+
add(nlist, marksN, thread.pc + 1, at + width, thread.caps);
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
// Between the halves of an astral character there is a position the engine
|
|
1123
|
+
// still looks at, and a zero-width pattern still matches there: `/\B/u`
|
|
1124
|
+
// against "a<astral>b" reports an empty match at the index INSIDE the pair.
|
|
1125
|
+
// Nothing that consumes can match there — the character does not begin at
|
|
1126
|
+
// that index — so only an empty match is looked for, and only after the
|
|
1127
|
+
// position before it has been settled, which keeps the leftmost one.
|
|
1128
|
+
if (matched === null && unicode && width === 2 && (!sticky || at + 1 === startAt)) {
|
|
1129
|
+
var splitList = [];
|
|
1130
|
+
var splitMarks = { seen: new Array(prog.length), gen: 1 };
|
|
1131
|
+
add(splitList, splitMarks, 0, at + 1, null);
|
|
1132
|
+
for (var sp = 0; sp < splitList.length; sp += 1) {
|
|
1133
|
+
if (prog[splitList[sp].pc].op === "m") { matched = splitList[sp].caps; break; }
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
if (at >= len) break;
|
|
1138
|
+
at += width;
|
|
1139
|
+
var swapList = clist;
|
|
1140
|
+
clist = nlist;
|
|
1141
|
+
nlist = swapList;
|
|
1142
|
+
nlist.length = 0;
|
|
1143
|
+
var swapMarks = marksC;
|
|
1144
|
+
marksC = marksN;
|
|
1145
|
+
marksN = swapMarks;
|
|
1146
|
+
marksN.gen += 1; // clear it for the next step
|
|
1147
|
+
}
|
|
1148
|
+
return matched === null ? null : _readCaptures(matched, slots);
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
// Read the chain back into slots, once, for the path that matched. Walking it
|
|
1152
|
+
// from the end means the FIRST value found for a slot is the most recent one
|
|
1153
|
+
// written, which is the one that counts; a clearing link answers for every slot
|
|
1154
|
+
// in its range that nothing later has already answered for.
|
|
1155
|
+
function _readCaptures(node, slots) {
|
|
1156
|
+
var out = new Array(slots);
|
|
1157
|
+
var known = new Array(slots);
|
|
1158
|
+
var i;
|
|
1159
|
+
for (i = 0; i < slots; i += 1) { out[i] = -1; known[i] = false; }
|
|
1160
|
+
var link = node;
|
|
1161
|
+
while (link !== null) {
|
|
1162
|
+
if (link.slot !== undefined) {
|
|
1163
|
+
if (!known[link.slot]) { out[link.slot] = link.at; known[link.slot] = true; }
|
|
1164
|
+
} else {
|
|
1165
|
+
for (var g = link.lo; g <= link.hi; g += 1) {
|
|
1166
|
+
var open = g * 2;
|
|
1167
|
+
var close = open + 1;
|
|
1168
|
+
if (!known[open]) { out[open] = -1; known[open] = true; }
|
|
1169
|
+
if (!known[close]) { out[close] = -1; known[close] = true; }
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
link = link.prev;
|
|
1173
|
+
}
|
|
1174
|
+
return out;
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
/**
|
|
1178
|
+
* @primitive b.regexLinear.compile
|
|
1179
|
+
* @signature b.regexLinear.compile(source, flags?)
|
|
1180
|
+
* @since 0.18.19
|
|
1181
|
+
* @status stable
|
|
1182
|
+
* @related b.guardRegex.assertSafe, b.guardRegex.validate
|
|
1183
|
+
*
|
|
1184
|
+
* Compile a pattern into a matcher that runs in time proportional to the
|
|
1185
|
+
* subject, whatever the pattern is.
|
|
1186
|
+
*
|
|
1187
|
+
* `source` is the pattern without delimiters, as `RegExp` takes it, or a
|
|
1188
|
+
* `RegExp` whose source and flags are used. Flags `i`, `m`, `s`, `u` and `y`
|
|
1189
|
+
* are honoured. `g` is not, because the matcher returns one match and the
|
|
1190
|
+
* caller decides what to do next.
|
|
1191
|
+
*
|
|
1192
|
+
* Two are refused by name rather than ignored, both under
|
|
1193
|
+
* `regex/unsupported-flag`. `v` brings class set operations — intersection,
|
|
1194
|
+
* subtraction, string properties — that this does not implement, and reading it
|
|
1195
|
+
* as a `u` would quietly match a different language; use `u`, or name the
|
|
1196
|
+
* characters in a plain class. `d` asks for the index of every capture, which
|
|
1197
|
+
* this does not record; drop it, or run the pattern with the platform engine
|
|
1198
|
+
* once it has been screened.
|
|
1199
|
+
*
|
|
1200
|
+
* The returned matcher exposes `test(subject)` and `exec(subject, from?)`.
|
|
1201
|
+
* `exec` returns `null` or a result shaped like the platform's: index 0 is the
|
|
1202
|
+
* whole match, higher indices are the capture groups, `index` is where the
|
|
1203
|
+
* match began, and `groups` carries the named ones.
|
|
1204
|
+
*
|
|
1205
|
+
* Backreferences and lookaround are refused at compile time, by name, with the
|
|
1206
|
+
* code `regex/unsupported-backreference` or `regex/unsupported-lookaround`.
|
|
1207
|
+
* They cannot be simulated without exploring paths one at a time, which is the
|
|
1208
|
+
* thing being avoided — so the choice is made where an operator can see it,
|
|
1209
|
+
* rather than by handing the pattern to an engine that can be made to hang.
|
|
1210
|
+
*
|
|
1211
|
+
* Four more are refused for narrower reasons, each under its own code and each
|
|
1212
|
+
* with a message saying what to write instead: a repetition whose body can
|
|
1213
|
+
* match nothing (`regex/nullable-repetition` — `(a*)*` means what `a*` means);
|
|
1214
|
+
* a group named outside ASCII, and a name reused across alternatives
|
|
1215
|
+
* (`regex/bad-group`); an inline flag modifier such as `(?i:...)`
|
|
1216
|
+
* (`regex/unsupported-group`), whose scoped flags this does not yet carry; and
|
|
1217
|
+
* a Unicode property escape under `u`, such as `\p{L}` (
|
|
1218
|
+
* `regex/unsupported-property`) — name the characters in a class instead.
|
|
1219
|
+
* Without `u` there is no property escape to refuse: `\p{L}` is a `p`, a brace,
|
|
1220
|
+
* an `L` and a brace there, and it compiles.
|
|
1221
|
+
* These are patterns the platform accepts, so a pattern using one has to be
|
|
1222
|
+
* screened and run with the platform engine, or rewritten. Everything else
|
|
1223
|
+
* accepted here returns exactly what `RegExp` returns, with one exception that
|
|
1224
|
+
* belongs to the platform rather than to this matcher: under `u`, an unanchored
|
|
1225
|
+
* pattern ending in a CHARACTER CLASS that can match at most one code point,
|
|
1226
|
+
* then `$`, misses a subject ending in an astral character.
|
|
1227
|
+
* `new RegExp("[^a]$", "u")` finds nothing in a single emoji, while
|
|
1228
|
+
* `new RegExp("^[^a]$", "u")` finds it — and a `^` can only take match
|
|
1229
|
+
* positions away, never add one. This returns the match, which is what the
|
|
1230
|
+
* language specifies, so on that one shape the two answers differ. A class that
|
|
1231
|
+
* may run on (`[^a]+$`, `[^a]*$`), a shorthand (`\W$`), the dot, and the
|
|
1232
|
+
* character written out are all unaffected.
|
|
1233
|
+
*
|
|
1234
|
+
* @opts
|
|
1235
|
+
* (none — flags are passed as the second argument, as `RegExp` takes them)
|
|
1236
|
+
*
|
|
1237
|
+
* @example
|
|
1238
|
+
* var m = b.regexLinear.compile("^(\\w+)@([\\w.]+)$");
|
|
1239
|
+
* m.test("ada@example.com"); // → true
|
|
1240
|
+
* m.exec("ada@example.com")[2]; // → "example.com"
|
|
1241
|
+
*
|
|
1242
|
+
* // The shape that hangs the platform engine runs in linear time here.
|
|
1243
|
+
* b.regexLinear.compile("(a+)+$").test("a".repeat(40) + "!"); // → false
|
|
1244
|
+
*/
|
|
1245
|
+
function compile(source, flags) {
|
|
1246
|
+
if (source instanceof RegExp) {
|
|
1247
|
+
flags = flags === undefined ? source.flags : flags;
|
|
1248
|
+
source = source.source;
|
|
1249
|
+
}
|
|
1250
|
+
if (typeof source !== "string") {
|
|
1251
|
+
throw new TypeError("regexLinear.compile: source must be a string or a RegExp");
|
|
1252
|
+
}
|
|
1253
|
+
if (flags === undefined || flags === null) flags = "";
|
|
1254
|
+
if (typeof flags !== "string") {
|
|
1255
|
+
throw new TypeError("regexLinear.compile: flags must be a string");
|
|
1256
|
+
}
|
|
1257
|
+
var flagProblem = _badFlags(flags);
|
|
1258
|
+
if (flagProblem !== null) {
|
|
1259
|
+
throw new TypeError("regexLinear.compile: " + flagProblem);
|
|
1260
|
+
}
|
|
1261
|
+
// (2) `v` brings set intersection, subtraction and nested classes. Reading
|
|
1262
|
+
// `[a&&b]` as ordinary members would match a different language than the
|
|
1263
|
+
// operator wrote, so the flag is refused until those are implemented rather
|
|
1264
|
+
// than accepted and approximated.
|
|
1265
|
+
if (flags.indexOf("d") !== -1) {
|
|
1266
|
+
throw new RegexLinearError("regex/unsupported-flag",
|
|
1267
|
+
"regexLinear.compile: the d flag promises match indices this does not " +
|
|
1268
|
+
"produce — drop it, or use the platform engine for that result");
|
|
1269
|
+
}
|
|
1270
|
+
if (flags.indexOf("v") !== -1) {
|
|
1271
|
+
throw new RegexLinearError("regex/unsupported-flag",
|
|
1272
|
+
"regexLinear.compile: the v flag brings class set operations that are not " +
|
|
1273
|
+
"implemented — use u, or name the characters in a plain class");
|
|
1274
|
+
}
|
|
1275
|
+
if (Buffer.byteLength(source, "utf8") > MAX_SOURCE_BYTES) {
|
|
1276
|
+
throw new RegexLinearError("regex/pattern-too-large",
|
|
1277
|
+
"regexLinear.compile: pattern longer than " + MAX_SOURCE_BYTES + " bytes");
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
var parsed = _parse(source, flags);
|
|
1281
|
+
var compiled = _compile(parsed.ast, parsed.captureCount);
|
|
1282
|
+
var runOpts = {
|
|
1283
|
+
icase: flags.indexOf("i") !== -1,
|
|
1284
|
+
multiline: flags.indexOf("m") !== -1,
|
|
1285
|
+
unicode: flags.indexOf("u") !== -1 || flags.indexOf("v") !== -1,
|
|
1286
|
+
sticky: flags.indexOf("y") !== -1,
|
|
1287
|
+
};
|
|
1288
|
+
|
|
1289
|
+
function exec(subject, from) {
|
|
1290
|
+
if (typeof subject !== "string") {
|
|
1291
|
+
throw new TypeError("regexLinear.exec: subject must be a string");
|
|
1292
|
+
}
|
|
1293
|
+
var startAt = from === undefined ? 0 : from;
|
|
1294
|
+
// A whole number of characters. A fraction reaches the string operations,
|
|
1295
|
+
// which truncate it, while the capture slots keep it — and the match comes
|
|
1296
|
+
// back claiming to start at index 1.5.
|
|
1297
|
+
if (typeof startAt !== "number" || !Number.isInteger(startAt) || startAt < 0) {
|
|
1298
|
+
throw new TypeError("regexLinear.exec: from must be a non-negative integer");
|
|
1299
|
+
}
|
|
1300
|
+
if (startAt > subject.length) return null;
|
|
1301
|
+
// Under `u` an offset that splits a surrogate pair does not name a place a
|
|
1302
|
+
// character starts. The platform's own answer there is not one rule — a
|
|
1303
|
+
// zero-width assertion can match at the split index while a consuming
|
|
1304
|
+
// pattern reports the index before it — so rather than pick one and differ
|
|
1305
|
+
// from `RegExp` silently, the offset is refused. A caller stepping through
|
|
1306
|
+
// matches under `u` advances by code point, and one that lands here has a
|
|
1307
|
+
// bug worth seeing.
|
|
1308
|
+
if (runOpts.unicode && startAt > 0 && startAt < subject.length) {
|
|
1309
|
+
var high = subject.charCodeAt(startAt - 1);
|
|
1310
|
+
var low = subject.charCodeAt(startAt);
|
|
1311
|
+
if (high >= 0xD800 && high <= 0xDBFF && low >= 0xDC00 && low <= 0xDFFF) {
|
|
1312
|
+
throw new RangeError("regexLinear.exec: from " + startAt + " splits a surrogate " +
|
|
1313
|
+
"pair — under the u flag an offset must fall where a character starts");
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
var caps = _run(compiled, subject, startAt, runOpts);
|
|
1317
|
+
if (caps === null) return null;
|
|
1318
|
+
var result = [];
|
|
1319
|
+
for (var g = 0; g <= parsed.captureCount; g += 1) {
|
|
1320
|
+
var lo = caps[g * 2];
|
|
1321
|
+
var hi = caps[g * 2 + 1];
|
|
1322
|
+
result[g] = (lo < 0 || hi < 0) ? undefined : subject.slice(lo, hi);
|
|
1323
|
+
}
|
|
1324
|
+
result.index = caps[0];
|
|
1325
|
+
result.input = subject;
|
|
1326
|
+
var named = Object.keys(parsed.names);
|
|
1327
|
+
if (named.length === 0) result.groups = undefined;
|
|
1328
|
+
else {
|
|
1329
|
+
var groups = Object.create(null);
|
|
1330
|
+
for (var n = 0; n < named.length; n += 1) groups[named[n]] = result[parsed.names[named[n]]];
|
|
1331
|
+
result.groups = groups;
|
|
1332
|
+
}
|
|
1333
|
+
return result;
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
return {
|
|
1337
|
+
source: source,
|
|
1338
|
+
flags: flags,
|
|
1339
|
+
groupCount: parsed.captureCount,
|
|
1340
|
+
groupNames: Object.keys(parsed.names),
|
|
1341
|
+
test: function (subject) { return exec(subject, 0) !== null; },
|
|
1342
|
+
exec: exec,
|
|
1343
|
+
};
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
module.exports = {
|
|
1347
|
+
compile: compile,
|
|
1348
|
+
RegexLinearError: RegexLinearError,
|
|
1349
|
+
};
|