regex-partial-match 1.3.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. package/README.md +120 -267
  2. package/lib/core/corePartialMatchRegExp.d.ts +24 -0
  3. package/lib/core/corePartialMatchRegExp.js +22 -0
  4. package/lib/core/index.d.ts +4 -0
  5. package/lib/core/index.js +2 -0
  6. package/lib/extend/index.d.ts +6 -7
  7. package/lib/extend/index.js +1 -1
  8. package/lib/features/index.d.ts +20 -0
  9. package/lib/features/index.js +23 -0
  10. package/lib/hitEnd/index.d.ts +45 -0
  11. package/lib/hitEnd/index.js +57 -0
  12. package/lib/hitEnd/matchHitEnd.d.ts +4 -0
  13. package/lib/hitEnd/matchHitEnd.js +51 -0
  14. package/lib/{partialMatchRegExp/isComplete → hitEnd}/partRole.d.ts +1 -1
  15. package/lib/hitEnd/partRole.js +17 -0
  16. package/lib/hitEnd/truncationProbe.d.ts +8 -0
  17. package/lib/hitEnd/truncationProbe.js +187 -0
  18. package/lib/hitEnd/truncationProbeCache.d.ts +14 -0
  19. package/lib/index.d.ts +4 -0
  20. package/lib/index.js +4 -0
  21. package/lib/modules/backreferences/asNativeAtom.d.ts +2 -0
  22. package/lib/{partialMatchRegExp/compilePartial → modules/backreferences}/asNativeAtom.js +2 -2
  23. package/lib/modules/backreferences/asPreScanPart.d.ts +2 -0
  24. package/lib/{partialMatchRegExp/compilePartial → modules/backreferences}/asPreScanPart.js +2 -2
  25. package/lib/modules/backreferences/backreferenceRecorder.d.ts +2 -0
  26. package/lib/modules/backreferences/backreferenceRecorder.js +38 -0
  27. package/lib/modules/backreferences/compileDynamic.d.ts +4 -0
  28. package/lib/modules/backreferences/compileDynamic.js +53 -0
  29. package/lib/modules/backreferences/compiledBackreferences.d.ts +9 -0
  30. package/lib/modules/backreferences/compiledBackreferences.js +9 -0
  31. package/lib/modules/backreferences/constants.d.ts +2 -0
  32. package/lib/modules/backreferences/constants.js +2 -0
  33. package/lib/modules/backreferences/execDynamic.d.ts +2 -0
  34. package/lib/modules/backreferences/execDynamic.js +43 -0
  35. package/lib/{partialMatchRegExp/compilePartial → modules/backreferences}/flagsAtBackreference.d.ts +1 -1
  36. package/lib/{partialMatchRegExp → modules/backreferences}/groupName.js +1 -1
  37. package/lib/modules/backreferences/index.d.ts +10 -0
  38. package/lib/modules/backreferences/index.js +20 -0
  39. package/lib/modules/backreferences/longestBakedPrefixEndingInput.js +46 -0
  40. package/lib/{partialMatchRegExp/compilePartial → modules/backreferences}/renderParts.d.ts +1 -1
  41. package/lib/{partialMatchRegExp/compilePartial → modules/backreferences}/renderParts.js +1 -1
  42. package/lib/{partialMatchRegExp/compilePartial → modules/backreferences}/resolvedFromScan.d.ts +1 -1
  43. package/lib/{partialMatchRegExp/compilePartial → modules/backreferences}/resolvedFromScan.js +2 -2
  44. package/lib/{partialMatchRegExp/compilePartial → modules/backreferences}/startsWithUnderFlags.js +1 -1
  45. package/lib/modules/carets/appendMultilineCaret.d.ts +7 -0
  46. package/lib/modules/carets/appendMultilineCaret.js +181 -0
  47. package/lib/modules/carets/caretRecorder.d.ts +2 -0
  48. package/lib/modules/carets/caretRecorder.js +164 -0
  49. package/lib/modules/carets/caretSyntax.d.ts +3 -0
  50. package/lib/modules/carets/caretSyntax.js +11 -0
  51. package/lib/modules/carets/index.d.ts +9 -0
  52. package/lib/modules/carets/index.js +18 -0
  53. package/lib/modules/carets/lineTerminator.d.ts +1 -0
  54. package/lib/modules/carets/lineTerminator.js +31 -0
  55. package/lib/modules/carets/needsCaretRules.d.ts +1 -0
  56. package/lib/modules/carets/needsCaretRules.js +15 -0
  57. package/lib/modules/carets/quantifierBounds.d.ts +3 -0
  58. package/lib/modules/carets/quantifierBounds.js +18 -0
  59. package/lib/modules/index.d.ts +3 -0
  60. package/lib/modules/index.js +2 -0
  61. package/lib/partialMatchRegExp/atomSyntax.d.ts +14 -1
  62. package/lib/partialMatchRegExp/atomSyntax.js +24 -1
  63. package/lib/partialMatchRegExp/backreferenceExpansion.d.ts +1 -6
  64. package/lib/partialMatchRegExp/backreferenceHook.d.ts +11 -0
  65. package/lib/partialMatchRegExp/backreferenceHook.js +1 -0
  66. package/lib/partialMatchRegExp/caretHook.d.ts +17 -0
  67. package/lib/partialMatchRegExp/caretHook.js +5 -0
  68. package/lib/partialMatchRegExp/compilePartial/compileWith.d.ts +3 -0
  69. package/lib/partialMatchRegExp/compilePartial/compileWith.js +36 -0
  70. package/lib/partialMatchRegExp/compilePartial/compiled.d.ts +17 -17
  71. package/lib/partialMatchRegExp/compilePartial/compiled.js +8 -21
  72. package/lib/partialMatchRegExp/compilePartial/constants.d.ts +4 -0
  73. package/lib/partialMatchRegExp/compilePartial/constants.js +4 -0
  74. package/lib/partialMatchRegExp/compilePartial/groupShape.d.ts +4 -0
  75. package/lib/partialMatchRegExp/compilePartial/groupShape.js +9 -0
  76. package/lib/partialMatchRegExp/compilePartial/toStatic.d.ts +2 -2
  77. package/lib/partialMatchRegExp/compilePartial/toStatic.js +2 -2
  78. package/lib/partialMatchRegExp/constants.d.ts +5 -0
  79. package/lib/partialMatchRegExp/constants.js +5 -0
  80. package/lib/partialMatchRegExp/createPartialMatchRegExp.d.ts +3 -0
  81. package/lib/partialMatchRegExp/createPartialMatchRegExp.js +21 -0
  82. package/lib/partialMatchRegExp/execFrom.d.ts +1 -0
  83. package/lib/partialMatchRegExp/execFrom.js +4 -0
  84. package/lib/partialMatchRegExp/index.d.ts +49 -3
  85. package/lib/partialMatchRegExp/index.js +31 -3
  86. package/lib/partialMatchRegExp/moduleHooks.d.ts +14 -0
  87. package/lib/partialMatchRegExp/moduleHooks.js +15 -0
  88. package/lib/partialMatchRegExp/opaqueModule.d.ts +9 -0
  89. package/lib/partialMatchRegExp/opaqueModule.js +1 -0
  90. package/lib/partialMatchRegExp/part.d.ts +2 -11
  91. package/lib/partialMatchRegExp/partialMatchInternals.d.ts +2 -1
  92. package/lib/partialMatchRegExp/partialMatchInternals.js +3 -1
  93. package/lib/partialMatchRegExp/quantifier.d.ts +6 -0
  94. package/lib/partialMatchRegExp/quantifier.js +9 -0
  95. package/lib/partialMatchRegExp/rawLookaroundInfo.d.ts +11 -0
  96. package/lib/partialMatchRegExp/rawLookaroundInfo.js +1 -0
  97. package/lib/partialMatchRegExp/regexFeatures.d.ts +1 -5
  98. package/lib/partialMatchRegExp/regexFeatures.js +2 -8
  99. package/lib/partialMatchRegExp/scope.d.ts +12 -0
  100. package/lib/partialMatchRegExp/scope.js +45 -0
  101. package/lib/partialMatchRegExp/walk.d.ts +13 -5
  102. package/lib/partialMatchRegExp/walk.js +211 -165
  103. package/lib/partialMatchRegExp/withModules.d.ts +13 -0
  104. package/lib/partialMatchRegExp/withModules.js +27 -0
  105. package/package.json +39 -56
  106. package/lib/partialMatchRegExp/compilePartial/asNativeAtom.d.ts +0 -2
  107. package/lib/partialMatchRegExp/compilePartial/asOptionalAtom.d.ts +0 -1
  108. package/lib/partialMatchRegExp/compilePartial/asOptionalAtom.js +0 -4
  109. package/lib/partialMatchRegExp/compilePartial/asPreScanPart.d.ts +0 -2
  110. package/lib/partialMatchRegExp/compilePartial/index.d.ts +0 -2
  111. package/lib/partialMatchRegExp/compilePartial/index.js +0 -79
  112. package/lib/partialMatchRegExp/compilePartial/longestBakedPrefixEndingInput.js +0 -50
  113. package/lib/partialMatchRegExp/compilePartial/reclassifyLegacyEscapes.d.ts +0 -2
  114. package/lib/partialMatchRegExp/compilePartial/reclassifyLegacyEscapes.js +0 -17
  115. package/lib/partialMatchRegExp/compilePartial/spliceOriginalSource.d.ts +0 -2
  116. package/lib/partialMatchRegExp/compilePartial/spliceOriginalSource.js +0 -12
  117. package/lib/partialMatchRegExp/isComplete/index.d.ts +0 -38
  118. package/lib/partialMatchRegExp/isComplete/index.js +0 -41
  119. package/lib/partialMatchRegExp/isComplete/isMatchComplete.d.ts +0 -3
  120. package/lib/partialMatchRegExp/isComplete/isMatchComplete.js +0 -14
  121. package/lib/partialMatchRegExp/isComplete/partRole.js +0 -28
  122. package/lib/partialMatchRegExp/isComplete/truncationProbe.d.ts +0 -8
  123. package/lib/partialMatchRegExp/isComplete/truncationProbe.js +0 -105
  124. package/lib/partialMatchRegExp/isComplete/truncationProbeCache.d.ts +0 -4
  125. package/lib/partialMatchRegExp/partialMatchRegExp.d.ts +0 -55
  126. package/lib/partialMatchRegExp/partialMatchRegExp.js +0 -114
  127. package/lib/partialMatchRegExp/preferLongerCaptures.d.ts +0 -1
  128. package/lib/partialMatchRegExp/preferLongerCaptures.js +0 -30
  129. /package/lib/{partialMatchRegExp/isComplete → hitEnd}/truncationProbeCache.js +0 -0
  130. /package/lib/{partialMatchRegExp/compilePartial → modules/backreferences}/caseFoldFlags.d.ts +0 -0
  131. /package/lib/{partialMatchRegExp/compilePartial → modules/backreferences}/caseFoldFlags.js +0 -0
  132. /package/lib/{partialMatchRegExp → modules/backreferences}/escapeAtom.d.ts +0 -0
  133. /package/lib/{partialMatchRegExp → modules/backreferences}/escapeAtom.js +0 -0
  134. /package/lib/{partialMatchRegExp/compilePartial → modules/backreferences}/flagsAtBackreference.js +0 -0
  135. /package/lib/{partialMatchRegExp → modules/backreferences}/groupName.d.ts +0 -0
  136. /package/lib/{partialMatchRegExp/compilePartial → modules/backreferences}/longestBakedPrefixEndingInput.d.ts +0 -0
  137. /package/lib/{partialMatchRegExp/compilePartial → modules/backreferences}/startsWithUnderFlags.d.ts +0 -0
@@ -0,0 +1,22 @@
1
+ import withModules from "../partialMatchRegExp/withModules.js";
2
+ /**
3
+ * `PartialMatchRegExp` without the rules that place a `^` in a group or after
4
+ * other parts under the `m` flag, or match a backreference, so that a bundle
5
+ * importing only `regex-partial-match/core` leaves them out.
6
+ *
7
+ * A pattern that needs either throws one `TypeError` naming every module it
8
+ * needs; every other pattern is transformed exactly as the class exported
9
+ * from `regex-partial-match` transforms it.
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * import PartialMatchRegExp from "regex-partial-match/core";
14
+ *
15
+ * new PartialMatchRegExp(/^hello world/).test("hello"); // true
16
+ * new PartialMatchRegExp(/(a)\1/); // throws TypeError
17
+ * ```
18
+ *
19
+ * @see {@link https://github.com/TomStrepsil/regex-partial-match#readme | Documentation}
20
+ */
21
+ const PartialMatchRegExp = withModules();
22
+ export default PartialMatchRegExp;
@@ -0,0 +1,4 @@
1
+ export { default } from "./corePartialMatchRegExp.ts";
2
+ export type { PartialMatchRegExp, PartialMatchRegExpConstructor } from "./corePartialMatchRegExp.ts";
3
+ export { default as withModules } from "../partialMatchRegExp/withModules.ts";
4
+ export type { Module } from "../partialMatchRegExp/opaqueModule.ts";
@@ -0,0 +1,2 @@
1
+ export { default } from "./corePartialMatchRegExp.js";
2
+ export { default as withModules } from "../partialMatchRegExp/withModules.js";
@@ -1,12 +1,11 @@
1
- import PartialMatchRegExp from "../partialMatchRegExp/partialMatchRegExp.ts";
1
+ import PartialMatchRegExp from "../partialMatchRegExp/index.ts";
2
2
  declare global {
3
3
  interface RegExp {
4
4
  /**
5
5
  * Transforms this regular expression to support partial matching.
6
6
  *
7
- * This method wraps each atomic element of the regex pattern in a non-capturing group
8
- * with an alternation to end-of-input (`$`), allowing the pattern to match prefixes
9
- * of the original pattern. This enables validation of incomplete input strings.
7
+ * The result matches any prefix of the original pattern, which enables
8
+ * validation of incomplete input strings.
10
9
  *
11
10
  * @returns A new PartialMatchRegExp that matches partial strings of the original pattern
12
11
  *
@@ -23,9 +22,9 @@ declare global {
23
22
  * ```
24
23
  *
25
24
  * @remarks
26
- * - The transformed regex will always match an empty string at the end of input
27
- * - Use with a start anchor (`^`) to prevent false positives from empty string matches
28
- * - The `y` (sticky) flag may not behave as expected in partial matching scenarios
25
+ * The transformed pattern always matches an empty string at the end of input;
26
+ * use a start anchor (`^`) to prevent false positives from empty string matches.
27
+ * See the documentation for how the `y` (sticky) flag behaves.
29
28
  *
30
29
  * @see {@link https://github.com/TomStrepsil/regex-partial-match#readme | Documentation}
31
30
  */
@@ -1,4 +1,4 @@
1
- import PartialMatchRegExp from "../partialMatchRegExp/partialMatchRegExp.js";
1
+ import PartialMatchRegExp from "../partialMatchRegExp/index.js";
2
2
  RegExp.prototype.toPartialMatchRegex = function () {
3
3
  return new PartialMatchRegExp(this);
4
4
  };
@@ -0,0 +1,20 @@
1
+ import type PartialMatchRegExp from "../partialMatchRegExp/index.ts";
2
+ import { type RegexFeature } from "../partialMatchRegExp/regexFeatures.ts";
3
+ export type { RegexFeature };
4
+ /**
5
+ * The syntactic constructs `partial`'s original pattern uses.
6
+ *
7
+ * The set iterates in `RegexFeature` declaration order, not the order the
8
+ * constructs appear in the pattern.
9
+ *
10
+ * @param partial - A `PartialMatchRegExp` instance, from any entry point
11
+ * @returns The features the original pattern contains
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * import PartialMatchRegExp, { features } from "regex-partial-match";
16
+ *
17
+ * features(new PartialMatchRegExp(/^[a-z]+/)).has("startAnchor"); // true
18
+ * ```
19
+ */
20
+ export default function features(partial: PartialMatchRegExp): ReadonlySet<RegexFeature>;
@@ -0,0 +1,23 @@
1
+ import { compiledOf } from "../partialMatchRegExp/partialMatchInternals.js";
2
+ import { featureSet } from "../partialMatchRegExp/regexFeatures.js";
3
+ /**
4
+ * The syntactic constructs `partial`'s original pattern uses.
5
+ *
6
+ * The set iterates in `RegexFeature` declaration order, not the order the
7
+ * constructs appear in the pattern.
8
+ *
9
+ * @param partial - A `PartialMatchRegExp` instance, from any entry point
10
+ * @returns The features the original pattern contains
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * import PartialMatchRegExp, { features } from "regex-partial-match";
15
+ *
16
+ * features(new PartialMatchRegExp(/^[a-z]+/)).has("startAnchor"); // true
17
+ * ```
18
+ */
19
+ export default function features(partial) {
20
+ var _a;
21
+ const compiled = compiledOf(partial);
22
+ return ((_a = compiled.features) !== null && _a !== void 0 ? _a : (compiled.features = featureSet(compiled.featureMask)));
23
+ }
@@ -0,0 +1,45 @@
1
+ import type PartialMatchRegExp from "../partialMatchRegExp/index.ts";
2
+ /**
3
+ * Whether the engine reached the end of the input while producing `match` —
4
+ * a match `partial` produced — so that more input could change it.
5
+ *
6
+ * The contract is that of the JDK's `Matcher.hitEnd()`: when this returns
7
+ * `false`, no continuation of the input changes the match's index or text,
8
+ * and its captures are the ones the original pattern produces, except for
9
+ * the two limits noted under `@remarks` below, where `false` is reported
10
+ * despite a read of the end. `true` means the match read the end of the
11
+ * input — an atom ran out of input, a greedy quantifier stopped there with
12
+ * nothing left to read, or `$`, `\b` or `\B` held there — so more input
13
+ * could extend it, change which alternative wins, or invalidate it, and
14
+ * its captures are the closest to what a full match would report rather
15
+ * than final.
16
+ *
17
+ * `false` is not "is a match": `/hello \w+/` matches `"hello world"` in full
18
+ * and still reports `true`, since `\w+` read the end looking for more. An
19
+ * exact-length match that consumed the last character without reading past
20
+ * it reports `false`.
21
+ *
22
+ * @param partial - The `PartialMatchRegExp` instance that produced `match`
23
+ * @param match - A match returned by `partial.exec()`
24
+ * @returns `true` when the match read the end of the input, `false` when it
25
+ * settled short of it
26
+ *
27
+ * @remarks
28
+ * Conservative where the JDK is exact in one place: a bounded greedy
29
+ * quantifier (`?`, `{n,m}`) fully taken at the end of the input reports
30
+ * `true` — on a group, and the same way for an unequal-bound `{n,m}`
31
+ * directly on a single atom once it's saturated at its maximum. Cannot see
32
+ * a read of the end inside a raw lookaround, or inside a lookahead in an
33
+ * earlier iteration of a quantified group.
34
+ *
35
+ * @example
36
+ * ```typescript
37
+ * import PartialMatchRegExp, { hitEnd } from "regex-partial-match";
38
+ *
39
+ * const partial = new PartialMatchRegExp(/^\d{4}-\d{2}-\d{2}/);
40
+ * const match = partial.exec('2024-06');
41
+ *
42
+ * match !== null && hitEnd(partial, match); // true - keep typing
43
+ * ```
44
+ */
45
+ export default function hitEnd(partial: PartialMatchRegExp, match: RegExpExecArray): boolean;
@@ -0,0 +1,57 @@
1
+ import { compiledOf } from "../partialMatchRegExp/partialMatchInternals.js";
2
+ import matchHitEnd from "./matchHitEnd.js";
3
+ /**
4
+ * Whether the engine reached the end of the input while producing `match` —
5
+ * a match `partial` produced — so that more input could change it.
6
+ *
7
+ * The contract is that of the JDK's `Matcher.hitEnd()`: when this returns
8
+ * `false`, no continuation of the input changes the match's index or text,
9
+ * and its captures are the ones the original pattern produces, except for
10
+ * the two limits noted under `@remarks` below, where `false` is reported
11
+ * despite a read of the end. `true` means the match read the end of the
12
+ * input — an atom ran out of input, a greedy quantifier stopped there with
13
+ * nothing left to read, or `$`, `\b` or `\B` held there — so more input
14
+ * could extend it, change which alternative wins, or invalidate it, and
15
+ * its captures are the closest to what a full match would report rather
16
+ * than final.
17
+ *
18
+ * `false` is not "is a match": `/hello \w+/` matches `"hello world"` in full
19
+ * and still reports `true`, since `\w+` read the end looking for more. An
20
+ * exact-length match that consumed the last character without reading past
21
+ * it reports `false`.
22
+ *
23
+ * @param partial - The `PartialMatchRegExp` instance that produced `match`
24
+ * @param match - A match returned by `partial.exec()`
25
+ * @returns `true` when the match read the end of the input, `false` when it
26
+ * settled short of it
27
+ *
28
+ * @remarks
29
+ * Conservative where the JDK is exact in one place: a bounded greedy
30
+ * quantifier (`?`, `{n,m}`) fully taken at the end of the input reports
31
+ * `true` — on a group, and the same way for an unequal-bound `{n,m}`
32
+ * directly on a single atom once it's saturated at its maximum. Cannot see
33
+ * a read of the end inside a raw lookaround, or inside a lookahead in an
34
+ * earlier iteration of a quantified group.
35
+ *
36
+ * @example
37
+ * ```typescript
38
+ * import PartialMatchRegExp, { hitEnd } from "regex-partial-match";
39
+ *
40
+ * const partial = new PartialMatchRegExp(/^\d{4}-\d{2}-\d{2}/);
41
+ * const match = partial.exec('2024-06');
42
+ *
43
+ * match !== null && hitEnd(partial, match); // true - keep typing
44
+ * ```
45
+ */
46
+ export default function hitEnd(partial, match) {
47
+ var _a, _b;
48
+ const compiled = compiledOf(partial);
49
+ return matchHitEnd(compiled, match, ((_a = compiled.probeCache) !== null && _a !== void 0 ? _a : (compiled.probeCache = {
50
+ rawLookarounds: (_b = compiled.rawLookarounds) !== null && _b !== void 0 ? _b : [],
51
+ flags: partial.flags,
52
+ probe: undefined,
53
+ stickyPreScan: undefined,
54
+ expansions: [],
55
+ oldestExpansion: 0
56
+ })));
57
+ }
@@ -0,0 +1,4 @@
1
+ import type { CompiledPartial } from "../partialMatchRegExp/compilePartial/compiled.ts";
2
+ import type { TruncationProbeCache } from "./truncationProbeCache.ts";
3
+ export declare const EXPANDED_PROBES_KEPT = 12;
4
+ export default function matchHitEnd(compiled: CompiledPartial, match: RegExpExecArray, cache: TruncationProbeCache): boolean;
@@ -0,0 +1,51 @@
1
+ import { buildTruncationProbe, tookTruncationBranch } from "./truncationProbe.js";
2
+ import { backreferenceExpansion } from "../partialMatchRegExp/backreferenceExpansion.js";
3
+ import { FLAGS_IRRELEVANT_TO_REBUILD } from "../partialMatchRegExp/constants.js";
4
+ export const EXPANDED_PROBES_KEPT = 12;
5
+ export default function matchHitEnd(compiled, match, cache) {
6
+ const probe = "dynamic" in compiled
7
+ ? dynamicProbe(compiled, match, cache)
8
+ : unexpandedProbe(compiled, cache);
9
+ return tookTruncationBranch(probe, match.input, match.index);
10
+ }
11
+ function dynamicProbe(compiled, match, cache) {
12
+ var _a;
13
+ const parts = (_a = match[backreferenceExpansion]) !== null && _a !== void 0 ? _a : expandedPartsAt(compiled, match, cache);
14
+ if (parts === undefined)
15
+ return unexpandedProbe(compiled, cache);
16
+ for (const expansion of cache.expansions) {
17
+ if (sameParts(expansion.parts, parts))
18
+ return expansion.probe;
19
+ }
20
+ const probe = probeOf(parts, cache);
21
+ cache.expansions[cache.oldestExpansion] = { parts, probe };
22
+ cache.oldestExpansion = (cache.oldestExpansion + 1) % EXPANDED_PROBES_KEPT;
23
+ return probe;
24
+ }
25
+ function unexpandedProbe(compiled, cache) {
26
+ var _a;
27
+ return ((_a = cache.probe) !== null && _a !== void 0 ? _a : (cache.probe = probeOf(compiled.parts, cache)));
28
+ }
29
+ function probeOf(parts, { rawLookarounds, flags }) {
30
+ return buildTruncationProbe(parts, rawLookarounds, flags);
31
+ }
32
+ function expandedPartsAt(compiled, match, cache) {
33
+ var _a;
34
+ const { preScan, expand } = compiled.dynamic;
35
+ (_a = cache.stickyPreScan) !== null && _a !== void 0 ? _a : (cache.stickyPreScan = new RegExp(preScan.source, cache.flags.replace(FLAGS_IRRELEVANT_TO_REBUILD, "") + "y"));
36
+ cache.stickyPreScan.lastIndex = match.index;
37
+ const capture = cache.stickyPreScan.exec(match.input);
38
+ if (capture === null)
39
+ return undefined;
40
+ const parts = expand(capture);
41
+ return parts.length === compiled.parts.length ? undefined : parts;
42
+ }
43
+ function sameParts(cached, parts) {
44
+ if (cached.length !== parts.length)
45
+ return false;
46
+ for (let index = 0; index < parts.length; index++) {
47
+ if (cached[index] !== parts[index])
48
+ return false;
49
+ }
50
+ return true;
51
+ }
@@ -1,3 +1,3 @@
1
- import { type Part } from "../part.ts";
1
+ import { type Part } from "../partialMatchRegExp/part.ts";
2
2
  export type PartRole = "backreference" | "rawLookaround" | "groupOpen" | "truncationEnd" | "plain";
3
3
  export declare function roleOf(part: Part): PartRole;
@@ -0,0 +1,17 @@
1
+ import { NAMED_GROUP_OPENING, endsAtTruncationBranch, isRawLookaround } from "../partialMatchRegExp/atomSyntax.js";
2
+ import { isBackreference } from "../partialMatchRegExp/part.js";
3
+ function isGroupOpen(part) {
4
+ return (part === "(" ||
5
+ (part.startsWith(NAMED_GROUP_OPENING) && !isRawLookaround(part)));
6
+ }
7
+ export function roleOf(part) {
8
+ if (isBackreference(part))
9
+ return "backreference";
10
+ if (isRawLookaround(part))
11
+ return "rawLookaround";
12
+ if (isGroupOpen(part))
13
+ return "groupOpen";
14
+ if (endsAtTruncationBranch(part))
15
+ return "truncationEnd";
16
+ return "plain";
17
+ }
@@ -0,0 +1,8 @@
1
+ import { type Part } from "../partialMatchRegExp/part.ts";
2
+ import type { RawLookarounds } from "../partialMatchRegExp/rawLookaroundInfo.ts";
3
+ export interface TruncationProbe {
4
+ regex: RegExp;
5
+ markerGroups: readonly number[];
6
+ }
7
+ export declare const buildTruncationProbe: (parts: readonly Part[], rawLookarounds: RawLookarounds, flags: string) => TruncationProbe;
8
+ export declare const tookTruncationBranch: (probe: TruncationProbe, input: string, index: number) => boolean;
@@ -0,0 +1,187 @@
1
+ import { legacyEscapeAsLiteral } from "../partialMatchRegExp/legacyEscape.js";
2
+ import { FLAGS_IRRELEVANT_TO_REBUILD } from "../partialMatchRegExp/constants.js";
3
+ import { DISJUNCTION_TO_END_OF_INPUT, END_ANCHOR, OPTIONAL_ATOM_OPENING, isOptionalAtom, isWordBoundaryAtom } from "../partialMatchRegExp/atomSyntax.js";
4
+ import { isQuantifier, quantifierEndingAt } from "../partialMatchRegExp/quantifier.js";
5
+ import { isBackreference, isNumericBackreference } from "../partialMatchRegExp/part.js";
6
+ import { roleOf } from "./partRole.js";
7
+ const END_OF_INPUT = DISJUNCTION_TO_END_OF_INPUT.slice(1, -1);
8
+ const OPTIONAL_QUANTIFIER = "?";
9
+ const EXACT_QUANTIFIER = /^\{0*([1-9]\d*|0)(?:,0*\1)?\}$/;
10
+ const MARKING = {
11
+ none: 0,
12
+ groupOpen: 1,
13
+ rawLookaround: 2,
14
+ truncationBranch: 3,
15
+ wordBoundary: 4,
16
+ endAnchor: 5,
17
+ readAtEnd: 6,
18
+ optionalAtEnd: 7
19
+ };
20
+ function isGreedyReadAtEnd(parts, index) {
21
+ const part = parts[index];
22
+ return (isQuantifier(part) &&
23
+ !EXACT_QUANTIFIER.test(part) &&
24
+ parts[index + 1] !== OPTIONAL_QUANTIFIER &&
25
+ quantifierEndingAt(parts, index) === index);
26
+ }
27
+ function markingOf(parts, index) {
28
+ const part = parts[index];
29
+ switch (roleOf(part)) {
30
+ case "backreference":
31
+ return MARKING.truncationBranch;
32
+ case "rawLookaround":
33
+ return MARKING.rawLookaround;
34
+ case "groupOpen":
35
+ return MARKING.groupOpen;
36
+ case "truncationEnd":
37
+ return isWordBoundaryAtom(part)
38
+ ? MARKING.wordBoundary
39
+ : MARKING.truncationBranch;
40
+ case "plain": {
41
+ if (part === END_ANCHOR)
42
+ return MARKING.endAnchor;
43
+ if (!isGreedyReadAtEnd(parts, index))
44
+ return MARKING.none;
45
+ const previous = parts[index - 1];
46
+ return part === OPTIONAL_QUANTIFIER &&
47
+ (isBackreference(previous) || isOptionalAtom(previous))
48
+ ? MARKING.optionalAtEnd
49
+ : MARKING.readAtEnd;
50
+ }
51
+ }
52
+ }
53
+ const markingsOf = (parts) => parts.map((_, index) => markingOf(parts, index));
54
+ function markersAddedBy(marking) {
55
+ switch (marking) {
56
+ case MARKING.none:
57
+ case MARKING.groupOpen:
58
+ case MARKING.rawLookaround:
59
+ return 0;
60
+ case MARKING.wordBoundary:
61
+ return 2;
62
+ default:
63
+ return 1;
64
+ }
65
+ }
66
+ function groupShiftTable(markings, rawLookarounds) {
67
+ var _a, _b;
68
+ const shiftForGroup = [0];
69
+ let markerCount = 0;
70
+ let rawLookaroundIndex = 0;
71
+ for (const marking of markings) {
72
+ if (marking === MARKING.groupOpen) {
73
+ shiftForGroup.push(markerCount);
74
+ }
75
+ else if (marking === MARKING.rawLookaround) {
76
+ const capturingGroupsOpened = (_b = (_a = rawLookarounds[rawLookaroundIndex++]) === null || _a === void 0 ? void 0 : _a.capturingGroupsOpened) !== null && _b !== void 0 ? _b : 0;
77
+ for (let opened = 0; opened < capturingGroupsOpened; opened++) {
78
+ shiftForGroup.push(markerCount);
79
+ }
80
+ }
81
+ markerCount += markersAddedBy(marking);
82
+ }
83
+ return shiftForGroup;
84
+ }
85
+ function renumberedToken(backreference, shiftForGroup) {
86
+ return isNumericBackreference(backreference)
87
+ ? "\\" + String(backreference.ref + shiftForGroup[backreference.ref])
88
+ : "\\k<" + backreference.ref + ">";
89
+ }
90
+ function renumberRawBackreferences(part, info, shiftForGroup) {
91
+ let renumbered = "";
92
+ let cursor = 0;
93
+ for (const reference of info.references) {
94
+ const relativeStart = reference.start - info.sourceStart;
95
+ const relativeEnd = reference.end - info.sourceStart;
96
+ renumbered +=
97
+ part.slice(cursor, relativeStart) +
98
+ rawReferenceReplacement(part.slice(relativeStart, relativeEnd), reference, shiftForGroup);
99
+ cursor = relativeEnd;
100
+ }
101
+ return renumbered + part.slice(cursor);
102
+ }
103
+ function rawReferenceReplacement(spelling, { ref }, shiftForGroup) {
104
+ return ref >= 1 && ref < shiftForGroup.length
105
+ ? "\\" + String(ref + shiftForGroup[ref])
106
+ : legacyEscapeAsLiteral(spelling.slice(1));
107
+ }
108
+ export const buildTruncationProbe = (parts, rawLookarounds, flags) => {
109
+ var _a;
110
+ const markings = markingsOf(parts);
111
+ const shiftForGroup = groupShiftTable(markings, rawLookarounds);
112
+ const markerGroups = [];
113
+ let groupsOpened = 0;
114
+ let rawLookaroundIndex = 0;
115
+ const marker = () => {
116
+ markerGroups.push(groupsOpened + markerGroups.length + 1);
117
+ return "()";
118
+ };
119
+ const truncationBranch = () => "|" + marker() + END_OF_INPUT + ")";
120
+ const readAtEnd = () => "(?:" + marker() + END_OF_INPUT + "|)";
121
+ const probed = [];
122
+ for (let index = 0; index < parts.length; index++) {
123
+ const part = parts[index];
124
+ if (isBackreference(part)) {
125
+ probed.push(OPTIONAL_ATOM_OPENING +
126
+ renumberedToken(part, shiftForGroup) +
127
+ truncationBranch());
128
+ continue;
129
+ }
130
+ switch (markings[index]) {
131
+ case MARKING.rawLookaround: {
132
+ const rawLookaround = rawLookarounds[rawLookaroundIndex++];
133
+ groupsOpened += (_a = rawLookaround === null || rawLookaround === void 0 ? void 0 : rawLookaround.capturingGroupsOpened) !== null && _a !== void 0 ? _a : 0;
134
+ probed.push(rawLookaround
135
+ ? renumberRawBackreferences(part, rawLookaround, shiftForGroup)
136
+ : part);
137
+ break;
138
+ }
139
+ case MARKING.groupOpen:
140
+ groupsOpened++;
141
+ probed.push(part);
142
+ break;
143
+ case MARKING.truncationBranch:
144
+ probed.push(part.slice(0, -DISJUNCTION_TO_END_OF_INPUT.length) + truncationBranch());
145
+ break;
146
+ case MARKING.wordBoundary:
147
+ probed.push(part.slice(0, -DISJUNCTION_TO_END_OF_INPUT.length) +
148
+ readAtEnd() +
149
+ truncationBranch());
150
+ break;
151
+ case MARKING.endAnchor:
152
+ probed.push(END_ANCHOR + "(?:" + marker() + "(?![\\s\\S])|)");
153
+ break;
154
+ case MARKING.readAtEnd:
155
+ probed.push(part + readAtEnd());
156
+ break;
157
+ case MARKING.optionalAtEnd:
158
+ probed[probed.length - 1] =
159
+ OPTIONAL_ATOM_OPENING +
160
+ probed[probed.length - 1] +
161
+ "|" +
162
+ marker() +
163
+ END_OF_INPUT +
164
+ "|)";
165
+ break;
166
+ default:
167
+ probed.push(part);
168
+ break;
169
+ }
170
+ }
171
+ return {
172
+ regex: new RegExp(probed.join(""), flags.replace(FLAGS_IRRELEVANT_TO_REBUILD, "") + "y"),
173
+ markerGroups
174
+ };
175
+ };
176
+ export const tookTruncationBranch = (probe, input, index) => {
177
+ const { regex, markerGroups } = probe;
178
+ regex.lastIndex = index;
179
+ const probed = regex.exec(input);
180
+ if (probed === null)
181
+ return true;
182
+ for (const group of markerGroups) {
183
+ if (probed[group] !== undefined)
184
+ return true;
185
+ }
186
+ return false;
187
+ };
@@ -0,0 +1,14 @@
1
+ import type { Part } from "../partialMatchRegExp/part.ts";
2
+ import type { RawLookarounds } from "../partialMatchRegExp/rawLookaroundInfo.ts";
3
+ import type { TruncationProbe } from "./truncationProbe.ts";
4
+ export interface TruncationProbeCache {
5
+ rawLookarounds: RawLookarounds;
6
+ flags: string;
7
+ probe: TruncationProbe | undefined;
8
+ stickyPreScan: RegExp | undefined;
9
+ expansions: {
10
+ parts: readonly Part[];
11
+ probe: TruncationProbe;
12
+ }[];
13
+ oldestExpansion: number;
14
+ }
package/lib/index.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ export * from "./partialMatchRegExp/index.ts";
2
+ export { default } from "./partialMatchRegExp/index.ts";
3
+ export { default as hitEnd } from "./hitEnd/index.ts";
4
+ export { default as features } from "./features/index.ts";
package/lib/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export * from "./partialMatchRegExp/index.js";
2
+ export { default } from "./partialMatchRegExp/index.js";
3
+ export { default as hitEnd } from "./hitEnd/index.js";
4
+ export { default as features } from "./features/index.js";
@@ -0,0 +1,2 @@
1
+ import { type Backreference } from "../../partialMatchRegExp/part.ts";
2
+ export default function asNativeAtom(backref: Backreference): string;
@@ -1,5 +1,5 @@
1
- import { isNumericBackreference } from "../part.js";
2
- import asOptionalAtom from "./asOptionalAtom.js";
1
+ import { isNumericBackreference } from "../../partialMatchRegExp/part.js";
2
+ import { asOptionalAtom } from "../../partialMatchRegExp/atomSyntax.js";
3
3
  function backrefToken(backref) {
4
4
  return isNumericBackreference(backref)
5
5
  ? "\\" + String(backref.ref)
@@ -0,0 +1,2 @@
1
+ import { type Part } from "../../partialMatchRegExp/part.ts";
2
+ export default function asPreScanPart(part: Part): string;
@@ -1,5 +1,5 @@
1
- import { ANY_CAPTURED_TEXT } from "../atomSyntax.js";
2
- import { isBackreference } from "../part.js";
1
+ import { ANY_CAPTURED_TEXT } from "./constants.js";
2
+ import { isBackreference } from "../../partialMatchRegExp/part.js";
3
3
  import asNativeAtom from "./asNativeAtom.js";
4
4
  export default function asPreScanPart(part) {
5
5
  if (!isBackreference(part))
@@ -0,0 +1,2 @@
1
+ import type { BackreferenceRecorder } from "../../partialMatchRegExp/backreferenceHook.ts";
2
+ export default function backreferenceRecorder(): BackreferenceRecorder;
@@ -0,0 +1,38 @@
1
+ import { decodeGroupName, groupNameOf } from "./groupName.js";
2
+ import { isNumericBackreference } from "../../partialMatchRegExp/part.js";
3
+ import { CASE_INSENSITIVE } from "../../partialMatchRegExp/scope.js";
4
+ const UNNAMED_GROUP_OPENING = "(";
5
+ export default function backreferenceRecorder() {
6
+ let closedGroupNumbers;
7
+ let referencesSinceLastClose;
8
+ return {
9
+ groupClosed(groupNumber, opening) {
10
+ (closedGroupNumbers !== null && closedGroupNumbers !== void 0 ? closedGroupNumbers : (closedGroupNumbers = new Set())).add(groupNumber);
11
+ if (opening === UNNAMED_GROUP_OPENING)
12
+ return;
13
+ const name = decodeGroupName(groupNameOf(opening));
14
+ const earlierReferences = referencesSinceLastClose === null || referencesSinceLastClose === void 0 ? void 0 : referencesSinceLastClose.get(name);
15
+ if (earlierReferences === undefined)
16
+ return;
17
+ for (const earlier of earlierReferences)
18
+ earlier.forward = true;
19
+ referencesSinceLastClose === null || referencesSinceLastClose === void 0 ? void 0 : referencesSinceLastClose.delete(name);
20
+ },
21
+ backreference(backreference, scope) {
22
+ backreference.caseInsensitive = (scope & CASE_INSENSITIVE) !== 0;
23
+ if (isNumericBackreference(backreference)) {
24
+ backreference.forward = !(closedGroupNumbers === null || closedGroupNumbers === void 0 ? void 0 : closedGroupNumbers.has(backreference.ref));
25
+ return;
26
+ }
27
+ const name = decodeGroupName(backreference.ref);
28
+ backreference.forward = false;
29
+ referencesSinceLastClose !== null && referencesSinceLastClose !== void 0 ? referencesSinceLastClose : (referencesSinceLastClose = new Map());
30
+ const references = referencesSinceLastClose.get(name);
31
+ if (references === undefined) {
32
+ referencesSinceLastClose.set(name, [backreference]);
33
+ return;
34
+ }
35
+ references.push(backreference);
36
+ }
37
+ };
38
+ }
@@ -0,0 +1,4 @@
1
+ import { type Backreference, type Part } from "../../partialMatchRegExp/part.ts";
2
+ import type { Hooks } from "../../partialMatchRegExp/walk.ts";
3
+ import { CompiledBackreferences } from "./compiledBackreferences.ts";
4
+ export default function compileDynamic(parts: readonly Part[], backreferences: readonly Backreference[], flags: string, isUnicode: boolean, featureMask: number, hooks: Hooks): CompiledBackreferences;
@@ -0,0 +1,53 @@
1
+ import escapeAtom from "./escapeAtom.js";
2
+ import { ALTERNATION } from "./constants.js";
3
+ import { GROUP_CLOSING, ONLY_AT_END_OF_INPUT, OPTIONAL_ATOM_OPENING, asOptionalAtom } from "../../partialMatchRegExp/atomSyntax.js";
4
+ import { isBackreference } from "../../partialMatchRegExp/part.js";
5
+ import { CompiledBackreferences } from "./compiledBackreferences.js";
6
+ import asPreScanPart from "./asPreScanPart.js";
7
+ import resolvedFromScan from "./resolvedFromScan.js";
8
+ import startsWithUnderFlags from "./startsWithUnderFlags.js";
9
+ import longestBakedPrefixEndingInput from "./longestBakedPrefixEndingInput.js";
10
+ import flagsAtBackreference from "./flagsAtBackreference.js";
11
+ export default function compileDynamic(parts, backreferences, flags, isUnicode, featureMask, hooks) {
12
+ const dynamic = {
13
+ preScan: new RegExp(parts.map(asPreScanPart).join(""), flags),
14
+ expansionFitsCaptures: (expandedFrom, match, input) => {
15
+ var _a;
16
+ for (const backref of backreferences) {
17
+ const baked = resolvedFromScan(backref, expandedFrom);
18
+ if (baked === undefined)
19
+ continue;
20
+ const resolved = (_a = resolvedFromScan(backref, match)) !== null && _a !== void 0 ? _a : "";
21
+ if (resolved === baked)
22
+ continue;
23
+ const backrefFlags = flagsAtBackreference(backref, flags);
24
+ const consumed = longestBakedPrefixEndingInput(baked, input, backrefFlags);
25
+ if (!startsWithUnderFlags(resolved, consumed, backrefFlags))
26
+ return false;
27
+ }
28
+ return true;
29
+ },
30
+ expand: (capture) => {
31
+ const expanded = [];
32
+ for (const part of parts) {
33
+ if (!isBackreference(part)) {
34
+ expanded.push(part);
35
+ continue;
36
+ }
37
+ const captured = resolvedFromScan(part, capture);
38
+ if (captured === undefined || captured === "") {
39
+ expanded.push(part);
40
+ continue;
41
+ }
42
+ const atoms = isUnicode ? Array.from(captured) : captured.split("");
43
+ expanded.push(OPTIONAL_ATOM_OPENING, part, ALTERNATION);
44
+ for (const atom of atoms) {
45
+ expanded.push(asOptionalAtom(escapeAtom(atom)));
46
+ }
47
+ expanded.push(ONLY_AT_END_OF_INPUT, GROUP_CLOSING);
48
+ }
49
+ return expanded;
50
+ }
51
+ };
52
+ return new CompiledBackreferences(dynamic, parts, featureMask, hooks);
53
+ }