regex-partial-match 1.2.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 (122) hide show
  1. package/README.md +169 -178
  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.d.ts → extend/index.d.ts} +6 -7
  7. package/lib/{extend.js → 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/hitEnd/partRole.d.ts +3 -0
  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/hitEnd/truncationProbeCache.js +1 -0
  20. package/lib/index.d.ts +4 -0
  21. package/lib/index.js +4 -0
  22. package/lib/modules/backreferences/asNativeAtom.d.ts +2 -0
  23. package/lib/modules/backreferences/asNativeAtom.js +10 -0
  24. package/lib/modules/backreferences/asPreScanPart.d.ts +2 -0
  25. package/lib/modules/backreferences/asPreScanPart.js +8 -0
  26. package/lib/modules/backreferences/backreferenceRecorder.d.ts +2 -0
  27. package/lib/modules/backreferences/backreferenceRecorder.js +38 -0
  28. package/lib/modules/backreferences/caseFoldFlags.d.ts +1 -0
  29. package/lib/modules/backreferences/caseFoldFlags.js +3 -0
  30. package/lib/modules/backreferences/compileDynamic.d.ts +4 -0
  31. package/lib/modules/backreferences/compileDynamic.js +53 -0
  32. package/lib/modules/backreferences/compiledBackreferences.d.ts +9 -0
  33. package/lib/modules/backreferences/compiledBackreferences.js +9 -0
  34. package/lib/modules/backreferences/constants.d.ts +2 -0
  35. package/lib/modules/backreferences/constants.js +2 -0
  36. package/lib/modules/backreferences/execDynamic.d.ts +2 -0
  37. package/lib/modules/backreferences/execDynamic.js +43 -0
  38. package/lib/modules/backreferences/flagsAtBackreference.d.ts +2 -0
  39. package/lib/modules/backreferences/flagsAtBackreference.js +4 -0
  40. package/lib/modules/backreferences/groupName.d.ts +2 -0
  41. package/lib/modules/backreferences/groupName.js +8 -0
  42. package/lib/modules/backreferences/index.d.ts +10 -0
  43. package/lib/modules/backreferences/index.js +20 -0
  44. package/lib/modules/backreferences/longestBakedPrefixEndingInput.d.ts +1 -0
  45. package/lib/modules/backreferences/longestBakedPrefixEndingInput.js +46 -0
  46. package/lib/modules/backreferences/renderParts.d.ts +2 -0
  47. package/lib/modules/backreferences/renderParts.js +9 -0
  48. package/lib/modules/backreferences/resolvedFromScan.d.ts +2 -0
  49. package/lib/modules/backreferences/resolvedFromScan.js +10 -0
  50. package/lib/modules/backreferences/startsWithUnderFlags.d.ts +1 -0
  51. package/lib/modules/backreferences/startsWithUnderFlags.js +7 -0
  52. package/lib/modules/carets/appendMultilineCaret.d.ts +7 -0
  53. package/lib/modules/carets/appendMultilineCaret.js +181 -0
  54. package/lib/modules/carets/caretRecorder.d.ts +2 -0
  55. package/lib/modules/carets/caretRecorder.js +164 -0
  56. package/lib/modules/carets/caretSyntax.d.ts +3 -0
  57. package/lib/modules/carets/caretSyntax.js +11 -0
  58. package/lib/modules/carets/index.d.ts +9 -0
  59. package/lib/modules/carets/index.js +18 -0
  60. package/lib/modules/carets/lineTerminator.d.ts +1 -0
  61. package/lib/modules/carets/lineTerminator.js +31 -0
  62. package/lib/modules/carets/needsCaretRules.d.ts +1 -0
  63. package/lib/modules/carets/needsCaretRules.js +15 -0
  64. package/lib/modules/carets/quantifierBounds.d.ts +3 -0
  65. package/lib/modules/carets/quantifierBounds.js +18 -0
  66. package/lib/modules/index.d.ts +3 -0
  67. package/lib/modules/index.js +2 -0
  68. package/lib/partialMatchRegExp/atomSyntax.d.ts +17 -0
  69. package/lib/partialMatchRegExp/atomSyntax.js +27 -0
  70. package/lib/partialMatchRegExp/backreferenceExpansion.d.ts +5 -0
  71. package/lib/partialMatchRegExp/backreferenceExpansion.js +1 -0
  72. package/lib/partialMatchRegExp/backreferenceHook.d.ts +11 -0
  73. package/lib/partialMatchRegExp/backreferenceHook.js +1 -0
  74. package/lib/partialMatchRegExp/caretHook.d.ts +17 -0
  75. package/lib/partialMatchRegExp/caretHook.js +5 -0
  76. package/lib/partialMatchRegExp/compilePartial/compileWith.d.ts +3 -0
  77. package/lib/partialMatchRegExp/compilePartial/compileWith.js +36 -0
  78. package/lib/partialMatchRegExp/compilePartial/compiled.d.ts +29 -0
  79. package/lib/partialMatchRegExp/compilePartial/compiled.js +14 -0
  80. package/lib/partialMatchRegExp/compilePartial/constants.d.ts +4 -0
  81. package/lib/partialMatchRegExp/compilePartial/constants.js +4 -0
  82. package/lib/partialMatchRegExp/compilePartial/groupShape.d.ts +4 -0
  83. package/lib/partialMatchRegExp/compilePartial/groupShape.js +9 -0
  84. package/lib/partialMatchRegExp/compilePartial/toStatic.d.ts +3 -0
  85. package/lib/partialMatchRegExp/compilePartial/toStatic.js +4 -0
  86. package/lib/partialMatchRegExp/constants.d.ts +5 -0
  87. package/lib/partialMatchRegExp/constants.js +5 -0
  88. package/lib/partialMatchRegExp/createPartialMatchRegExp.d.ts +3 -0
  89. package/lib/partialMatchRegExp/createPartialMatchRegExp.js +21 -0
  90. package/lib/partialMatchRegExp/execFrom.d.ts +1 -0
  91. package/lib/partialMatchRegExp/execFrom.js +4 -0
  92. package/lib/partialMatchRegExp/index.d.ts +49 -0
  93. package/lib/partialMatchRegExp/index.js +31 -0
  94. package/lib/partialMatchRegExp/legacyEscape.d.ts +2 -0
  95. package/lib/partialMatchRegExp/legacyEscape.js +18 -0
  96. package/lib/partialMatchRegExp/moduleHooks.d.ts +14 -0
  97. package/lib/partialMatchRegExp/moduleHooks.js +15 -0
  98. package/lib/partialMatchRegExp/opaqueModule.d.ts +9 -0
  99. package/lib/partialMatchRegExp/opaqueModule.js +1 -0
  100. package/lib/partialMatchRegExp/part.d.ts +15 -0
  101. package/lib/partialMatchRegExp/part.js +2 -0
  102. package/lib/partialMatchRegExp/partialMatchInternals.d.ts +3 -0
  103. package/lib/partialMatchRegExp/partialMatchInternals.js +4 -0
  104. package/lib/partialMatchRegExp/quantifier.d.ts +6 -0
  105. package/lib/partialMatchRegExp/quantifier.js +9 -0
  106. package/lib/partialMatchRegExp/rawLookaroundInfo.d.ts +11 -0
  107. package/lib/partialMatchRegExp/rawLookaroundInfo.js +1 -0
  108. package/lib/partialMatchRegExp/regexFeatures.d.ts +2 -0
  109. package/lib/partialMatchRegExp/regexFeatures.js +41 -0
  110. package/lib/partialMatchRegExp/scope.d.ts +12 -0
  111. package/lib/partialMatchRegExp/scope.js +45 -0
  112. package/lib/partialMatchRegExp/walk.d.ts +16 -0
  113. package/lib/partialMatchRegExp/walk.js +412 -0
  114. package/lib/partialMatchRegExp/withModules.d.ts +13 -0
  115. package/lib/partialMatchRegExp/withModules.js +27 -0
  116. package/package.json +46 -53
  117. package/lib/compilePartial.d.ts +0 -26
  118. package/lib/compilePartial.js +0 -397
  119. package/lib/partialMatchRegExp.d.ts +0 -52
  120. package/lib/partialMatchRegExp.js +0 -126
  121. /package/lib/{escapeAtom.d.ts → modules/backreferences/escapeAtom.d.ts} +0 -0
  122. /package/lib/{escapeAtom.js → modules/backreferences/escapeAtom.js} +0 -0
@@ -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
+ }
@@ -0,0 +1,3 @@
1
+ import { type Part } from "../partialMatchRegExp/part.ts";
2
+ export type PartRole = "backreference" | "rawLookaround" | "groupOpen" | "truncationEnd" | "plain";
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
+ }
@@ -0,0 +1 @@
1
+ export {};
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;
@@ -0,0 +1,10 @@
1
+ import { isNumericBackreference } from "../../partialMatchRegExp/part.js";
2
+ import { asOptionalAtom } from "../../partialMatchRegExp/atomSyntax.js";
3
+ function backrefToken(backref) {
4
+ return isNumericBackreference(backref)
5
+ ? "\\" + String(backref.ref)
6
+ : "\\k<" + backref.ref + ">";
7
+ }
8
+ export default function asNativeAtom(backref) {
9
+ return asOptionalAtom(backrefToken(backref));
10
+ }
@@ -0,0 +1,2 @@
1
+ import { type Part } from "../../partialMatchRegExp/part.ts";
2
+ export default function asPreScanPart(part: Part): string;
@@ -0,0 +1,8 @@
1
+ import { ANY_CAPTURED_TEXT } from "./constants.js";
2
+ import { isBackreference } from "../../partialMatchRegExp/part.js";
3
+ import asNativeAtom from "./asNativeAtom.js";
4
+ export default function asPreScanPart(part) {
5
+ if (!isBackreference(part))
6
+ return part;
7
+ return part.forward ? asNativeAtom(part) : ANY_CAPTURED_TEXT;
8
+ }
@@ -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 @@
1
+ export default function caseFoldFlags(flags: string): string;
@@ -0,0 +1,3 @@
1
+ export default function caseFoldFlags(flags) {
2
+ return flags.replace(/[^iuv]/g, "");
3
+ }
@@ -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
+ }
@@ -0,0 +1,9 @@
1
+ import { Compiled, type CompiledDynamic, type DynamicPath } from "../../partialMatchRegExp/compilePartial/compiled.ts";
2
+ import type { Part } from "../../partialMatchRegExp/part.ts";
3
+ import type { Hooks } from "../../partialMatchRegExp/walk.ts";
4
+ import execDynamic from "./execDynamic.ts";
5
+ export declare class CompiledBackreferences extends Compiled implements CompiledDynamic {
6
+ readonly dynamic: DynamicPath;
7
+ readonly execDynamic: typeof execDynamic;
8
+ constructor(dynamic: DynamicPath, parts: readonly Part[], featureMask: number, hooks: Hooks);
9
+ }
@@ -0,0 +1,9 @@
1
+ import { Compiled } from "../../partialMatchRegExp/compilePartial/compiled.js";
2
+ import execDynamic from "./execDynamic.js";
3
+ export class CompiledBackreferences extends Compiled {
4
+ constructor(dynamic, parts, featureMask, hooks) {
5
+ super(parts, featureMask, hooks);
6
+ this.dynamic = dynamic;
7
+ this.execDynamic = execDynamic;
8
+ }
9
+ }
@@ -0,0 +1,2 @@
1
+ export declare const ANY_CAPTURED_TEXT = "(?:[\\s\\S]*?)";
2
+ export declare const ALTERNATION = "|";
@@ -0,0 +1,2 @@
1
+ export const ANY_CAPTURED_TEXT = "(?:[\\s\\S]*?)";
2
+ export const ALTERNATION = "|";
@@ -0,0 +1,2 @@
1
+ import type { DynamicPath } from "../../partialMatchRegExp/compilePartial/compiled.ts";
2
+ export default function execDynamic(this: RegExp, dynamic: DynamicPath, input: string): RegExpExecArray | null;
@@ -0,0 +1,43 @@
1
+ import renderParts from "./renderParts.js";
2
+ import { backreferenceExpansion } from "../../partialMatchRegExp/backreferenceExpansion.js";
3
+ import execFrom from "../../partialMatchRegExp/execFrom.js";
4
+ function isAtOrBefore(match, index) {
5
+ return match !== null && match.index <= index;
6
+ }
7
+ export default function execDynamic(dynamic, input) {
8
+ const { preScan, expand, expansionFitsCaptures } = dynamic;
9
+ const honoursLastIndex = this.global || this.sticky;
10
+ const start = honoursLastIndex ? this.lastIndex : 0;
11
+ const originalMatch = RegExp.prototype.exec.call(this, input);
12
+ if (isAtOrBefore(originalMatch, start))
13
+ return originalMatch;
14
+ let preScanMatch = null;
15
+ if (originalMatch) {
16
+ preScanMatch = execFrom(preScan, input, start);
17
+ const noEarlierPartialPossible = preScanMatch === null || preScanMatch.index >= originalMatch.index;
18
+ if (noEarlierPartialPossible)
19
+ return originalMatch;
20
+ }
21
+ const capture = preScanMatch !== null && preScanMatch !== void 0 ? preScanMatch : execFrom(preScan, input, start);
22
+ if (capture === null)
23
+ return originalMatch;
24
+ const scanningFlags = honoursLastIndex ? this.flags : this.flags + "g";
25
+ let expandedFrom = capture;
26
+ let expandedParts = expand(expandedFrom);
27
+ let expanded = new RegExp(renderParts(expandedParts), scanningFlags);
28
+ let match = execFrom(expanded, input, start);
29
+ if (match !== null && !expansionFitsCaptures(expandedFrom, match, input)) {
30
+ expandedFrom = match;
31
+ expandedParts = expand(expandedFrom);
32
+ expanded = new RegExp(renderParts(expandedParts), scanningFlags);
33
+ match = execFrom(expanded, input, expandedFrom.index);
34
+ if (match !== null && !expansionFitsCaptures(expandedFrom, match, input))
35
+ match = null;
36
+ }
37
+ if (match === null || isAtOrBefore(originalMatch, match.index))
38
+ return originalMatch;
39
+ if (honoursLastIndex)
40
+ this.lastIndex = expanded.lastIndex;
41
+ match[backreferenceExpansion] = expandedParts;
42
+ return match;
43
+ }
@@ -0,0 +1,2 @@
1
+ import type { Backreference } from "../../partialMatchRegExp/part.ts";
2
+ export default function flagsAtBackreference(backref: Backreference, flags: string): string;
@@ -0,0 +1,4 @@
1
+ export default function flagsAtBackreference(backref, flags) {
2
+ const unicodeMode = flags.includes("v") ? "v" : flags.includes("u") ? "u" : "";
3
+ return (backref.caseInsensitive ? "i" : "") + unicodeMode;
4
+ }
@@ -0,0 +1,2 @@
1
+ export declare const groupNameOf: (namedGroupOpening: string) => string;
2
+ export declare function decodeGroupName(rawName: string): string;