first-commit
Some checks failed
CI Pipeline / build (push) Failing after 3m23s

This commit is contained in:
2025-08-27 14:05:33 +08:00
commit 9e1b8bdc9d
5159 changed files with 1081326 additions and 0 deletions

21
node_modules/regex-recursion/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 Steven Levithan
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

151
node_modules/regex-recursion/README.md generated vendored Normal file
View File

@@ -0,0 +1,151 @@
# regex-recursion
[![npm version][npm-version-src]][npm-version-href]
[![npm downloads][npm-downloads-src]][npm-downloads-href]
[![bundle][bundle-src]][bundle-href]
This is an official plugin for [Regex+](https://github.com/slevithan/regex) that adds support for recursive matching up to a specified max depth *N*, where *N* can be between 2 and 100. Generated regexes are native JavaScript `RegExp` instances.
> [!NOTE]
> Regex flavors vary on whether they offer infinite or fixed-depth recursion. For example, recursion in Oniguruma uses a depth limit of 20, and doesn't allow changing this.
Recursive matching is added to a regex via one of the following (the recursion depth limit is provided in place of *`N`*):
- `(?R=N)` — Recursively match the entire regex at this position.
- `\g<name&R=N>` or `\g<number&R=N>` — Recursively match the contents of the group referenced by name or number at this position.
- The `\g` subroutine must be *within* the referenced group.
Multiple uses of recursion within the same pattern are allowed if they are non-overlapping. Named captures and backreferences are supported within recursion, and are independent per depth level. So e.g. `groups.name` on a match object is the value captured by group `name` at the top level of the recursion stack.
## Install and use
```sh
npm install regex regex-recursion
```
```js
import {regex} from 'regex';
import {recursion} from 'regex-recursion';
const re = regex({plugins: [recursion]})`…`;
```
<details>
<summary>Using a global name (no import)</summary>
```html
<script src="https://cdn.jsdelivr.net/npm/regex@6.0.1/dist/regex.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/regex-recursion@6.0.2/dist/regex-recursion.min.js"></script>
<script>
const {regex} = Regex;
const {recursion} = Regex.plugins;
const re = regex({plugins: [recursion]})`…`;
</script>
```
</details>
## Examples
### Match an equal number of two different subpatterns
#### Anywhere within a string
```js
// Matches sequences of up to 20 'a' chars followed by the same number of 'b'
const re = regex({plugins: [recursion]})`a(?R=20)?b`;
re.exec('test aaaaaabbb')[0];
// → 'aaabbb'
```
#### As the entire string
Use `\g<name&R=N>` to recursively match just the specified group.
```js
const re = regex({plugins: [recursion]})`
^ (?<r> a \g<r&R=20>? b) $
`;
re.test('aaabbb'); // → true
re.test('aaabb'); // → false
```
### Match balanced parentheses
```js
// Matches all balanced parentheses up to depth 20
const parens = regex({flags: 'g', plugins: [recursion]})`
\( ([^\(\)] | (?R=20))* \)
`;
'test ) (balanced ((parens))) () ((a)) ( (b)'.match(parens);
/* → [
'(balanced ((parens)))',
'()',
'((a))',
'(b)'
] */
```
Following is an alternative that matches the same strings, but adds a nested quantifier. It then uses an atomic group to prevent this nested quantifier from creating the potential for [catastrophic backtracking](https://www.regular-expressions.info/catastrophic.html). Since the example above doesn't need a nested quantifier, this is not an improvement but merely an alternative that shows how to deal with the general problem of nested quantifiers with multiple ways to divide matches of the same strings.
```js
const parens = regex({flags: 'g', plugins: [recursion]})`
\( ((?> [^\(\)]+) | (?R=20))* \)
`;
// Or with a possessive quantifier
const parens = regex({flags: 'g', plugins: [recursion]})`
\( ([^\(\)]++ | (?R=20))* \)
`;
```
The first example above matches sequences of non-parentheses in one step with the nested `+` quantifier, and avoids backtracking into these sequences by wrapping it with an atomic group `(?>…)`. Given that what the nested quantifier `+` matches overlaps with what the outer group can match with its `*` quantifier, the atomic group is important here. It avoids exponential backtracking when matching long strings with unbalanced parentheses.
In cases where you're you're repeating a single token within an atomic group, possessive quantifiers provide syntax sugar.
Atomic groups and possessive quantifiers are provided by the base Regex+ library.
### Match palindromes
#### Match palindromes anywhere within a string
```js
const palindromes = regex({flags: 'gi', plugins: [recursion]})`
(?<char> \w)
# Recurse, or match a lone unbalanced char in the middle
((?R=15) | \w?)
\k<char>
`;
'Racecar, ABBA, and redivided'.match(palindromes);
// → ['Racecar', 'ABBA', 'edivide']
```
Palindromes are sequences that read the same backwards as forwards. In the example above, the max length of matched palindromes is 31. That's because it sets the max recursion depth to 15 with `(?R=15)`. So, depth 15 × 2 chars (left + right) for each depth level + 1 optional unbalanced char in the middle = 31. To match longer palindromes, the max recursion depth can be increased to a max of 100, which would enable matching palindromes up to 201 characters long.
#### Match palindromes as complete words
```js
const palindromeWords = regex({flags: 'gi', plugins: [recursion]})`
\b
(?<palindrome>
(?<char> \w)
(\g<palindrome&R=15> | \w?)
\k<char>
)
\b
`;
'Racecar, ABBA, and redivided'.match(palindromeWords);
// → ['Racecar', 'ABBA']
```
<!-- Badges -->
[npm-version-src]: https://img.shields.io/npm/v/regex-recursion?color=78C372
[npm-version-href]: https://npmjs.com/package/regex-recursion
[npm-downloads-src]: https://img.shields.io/npm/dm/regex-recursion?color=78C372
[npm-downloads-href]: https://npmjs.com/package/regex-recursion
[bundle-src]: https://img.shields.io/bundlejs/size/regex-recursion?color=78C372&label=minzip
[bundle-href]: https://bundlejs.com/?q=regex-recursion&treeshake=[*]

View File

@@ -0,0 +1,2 @@
var Regex;(Regex||={}).plugins=(()=>{var N=Object.defineProperty;var q=Object.getOwnPropertyDescriptor;var y=Object.getOwnPropertyNames;var J=Object.prototype.hasOwnProperty;var K=(e,t)=>{for(var n in t)N(e,n,{get:t[n],enumerable:!0})},Q=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of y(t))!J.call(e,o)&&o!==n&&N(e,o,{get:()=>t[o],enumerable:!(r=q(t,o))||r.enumerable});return e};var V=e=>Q(N({},"__esModule",{value:!0}),e);var ne={};K(ne,{recursion:()=>Z});var m=Object.freeze({DEFAULT:"DEFAULT",CHAR_CLASS:"CHAR_CLASS"});function T(e,t,n,r){let o=new RegExp(String.raw`${t}|(?<$skip>\[\^?|\\?.)`,"gsu"),u=[!1],s=0,c="";for(let i of e.matchAll(o)){let{0:p,groups:{$skip:f}}=i;if(!f&&(!r||r===m.DEFAULT==!s)){n instanceof Function?c+=n(i,{context:s?m.CHAR_CLASS:m.DEFAULT,negated:u[u.length-1]}):c+=n;continue}p[0]==="["?(s++,u.push(p[1]==="^")):p==="]"&&s&&(s--,u.pop()),c+=p}return c}function F(e,t,n,r){T(e,t,n,r)}function X(e,t,n=0,r){if(!new RegExp(t,"su").test(e))return null;let o=new RegExp(`${t}|(?<$skip>\\\\?.)`,"gsu");o.lastIndex=n;let u=0,s;for(;s=o.exec(e);){let{0:c,groups:{$skip:i}}=s;if(!i&&(!r||r===m.DEFAULT==!u))return s;c==="["?u++:c==="]"&&u&&u--,o.lastIndex==s.index&&o.lastIndex++}return null}function k(e,t,n){return!!X(e,t,0,n)}function G(e,t){let n=/\\?./gsu;n.lastIndex=t;let r=e.length,o=0,u=1,s;for(;s=n.exec(e);){let[c]=s;if(c==="[")o++;else if(o)c==="]"&&o--;else if(c==="(")u++;else if(c===")"&&(u--,!u)){r=s.index;break}}return e.slice(t,r)}var w=String.raw,Y=w`\\g<(?<gRNameOrNum>[^>&]+)&R=(?<gRDepth>[^>]+)>`,I=w`\(\?R=(?<rDepth>[^\)]+)\)|${Y}`,L=w`\(\?<(?![=!])(?<captureName>[^>]+)>`,_=w`${L}|(?<unnamed>\()(?!\?)`,x=new RegExp(w`${L}|${I}|\(\?|\\?.`,"gsu"),b="Cannot use multiple overlapping recursions";function Z(e,t){let{hiddenCaptures:n,mode:r}={hiddenCaptures:[],mode:"plugin",...t},o=t?.captureTransfers??new Map;if(!new RegExp(I,"su").test(e))return{pattern:e,captureTransfers:o,hiddenCaptures:n};if(r==="plugin"&&k(e,w`\(\?\(DEFINE\)`,m.DEFAULT))throw new Error("DEFINE groups cannot be used with recursion");let u=[],s=k(e,w`\\[1-9]`,m.DEFAULT),c=new Map,i=[],p=!1,f=0,a=0,$;for(x.lastIndex=0;$=x.exec(e);){let{0:g,groups:{captureName:d,rDepth:h,gRNameOrNum:l,gRDepth:R}}=$;if(g==="[")f++;else if(f)g==="]"&&f--;else if(h){if(B(h),p)throw new Error(b);if(s)throw new Error(`${r==="external"?"Backrefs":"Numbered backrefs"} cannot be used with global recursion`);let C=e.slice(0,$.index),E=e.slice(x.lastIndex);if(k(E,I,m.DEFAULT))throw new Error(b);let D=+h-1;e=H(C,E,D,!1,n,u,a),o=W(o,C,D,u.length,0,a);break}else if(l){B(R);let C=!1;for(let U of i)if(U.name===l||U.num===+l){if(C=!0,U.hasRecursedWithin)throw new Error(b);break}if(!C)throw new Error(w`Recursive \g cannot be used outside the referenced group "${r==="external"?l:w`\g<${l}&R=${R}>`}"`);let E=c.get(l),D=G(e,E);if(s&&k(D,w`${L}|\((?!\?)`,m.DEFAULT))throw new Error(`${r==="external"?"Backrefs":"Numbered backrefs"} cannot be used with recursion of capturing groups`);let A=e.slice(E,$.index),S=D.slice(A.length+g.length),O=u.length,M=+R-1,v=H(A,S,M,!0,n,u,a);o=W(o,A,M,u.length-O,O,a);let z=e.slice(0,E),j=e.slice(E+D.length);e=`${z}${v}${j}`,x.lastIndex+=v.length-g.length-A.length-S.length,i.forEach(U=>U.hasRecursedWithin=!0),p=!0}else if(d)a++,c.set(String(a),x.lastIndex),c.set(d,x.lastIndex),i.push({num:a,name:d});else if(g[0]==="("){let C=g==="(";C&&(a++,c.set(String(a),x.lastIndex)),i.push(C?{num:a}:{})}else g===")"&&i.pop()}return n.push(...u),{pattern:e,captureTransfers:o,hiddenCaptures:n}}function B(e){let t=`Max depth must be integer between 2 and 100; used ${e}`;if(!/^[1-9]\d*$/.test(e))throw new Error(t);if(e=+e,e<2||e>100)throw new Error(t)}function H(e,t,n,r,o,u,s){let c=new Set;r&&F(e+t,L,({groups:{captureName:p}})=>{c.add(p)},m.DEFAULT);let i=[n,r?c:null,o,u,s];return`${e}${P(`(?:${e}`,"forward",...i)}(?:)${P(`${t})`,"backward",...i)}${t}`}function P(e,t,n,r,o,u,s){let i=f=>t==="forward"?f+2:n-f+2-1,p="";for(let f=0;f<n;f++){let a=i(f);p+=T(e,w`${_}|\\k<(?<backref>[^>]+)>`,({0:$,groups:{captureName:g,unnamed:d,backref:h}})=>{if(h&&r&&!r.has(h))return $;let l=`_$${a}`;if(d||g){let R=s+u.length+1;return u.push(R),ee(o,R),d?$:`(?<${g}${l}>`}return w`\k<${h}${l}>`},m.DEFAULT)}return p}function ee(e,t){for(let n=0;n<e.length;n++)e[n]>=t&&e[n]++}function W(e,t,n,r,o,u){if(e.size&&r){let s=0;F(t,_,()=>s++,m.DEFAULT);let c=u-s+o,i=new Map;return e.forEach((p,f)=>{let a=(r-s*n)/n,$=s*n,g=f>c+s?f+r:f,d=[];for(let h of p)if(h<=c)d.push(h);else if(h>c+s+a)d.push(h+r);else if(h<=c+s)for(let l=0;l<=n;l++)d.push(h+s*l);else for(let l=0;l<=n;l++)d.push(h+$+a*l);i.set(g,d)}),i}return e}return V(ne);})();
//# sourceMappingURL=regex-recursion.min.js.map

File diff suppressed because one or more lines are too long

47
node_modules/regex-recursion/package.json generated vendored Normal file
View File

@@ -0,0 +1,47 @@
{
"name": "regex-recursion",
"version": "6.0.2",
"description": "Recursive matching plugin for Regex+",
"author": "Steven Levithan",
"license": "MIT",
"type": "module",
"exports": {
".": {
"types": "./types/index.d.ts",
"import": "./src/index.js"
}
},
"browser": "./dist/regex-recursion.min.js",
"types": "./types/index.d.ts",
"files": [
"dist",
"src",
"types"
],
"repository": {
"type": "git",
"url": "git+https://github.com/slevithan/regex-recursion.git"
},
"keywords": [
"recursion",
"regex",
"regexp"
],
"dependencies": {
"regex-utilities": "^2.3.0"
},
"devDependencies": {
"esbuild": "^0.24.2",
"jasmine": "^5.5.0",
"regex": "^6.0.1",
"typescript": "^5.7.3"
},
"scripts": {
"bundle:global": "esbuild src/index.js --global-name=Regex.plugins --bundle --minify --sourcemap --outfile=dist/regex-recursion.min.js",
"types": "tsc src/index.js --rootDir src --declaration --allowJs --emitDeclarationOnly --outDir types",
"prebuild": "rm -rf dist/* types/*",
"build": "pnpm run bundle:global && pnpm run types",
"pretest": "pnpm run build",
"test": "jasmine"
}
}

365
node_modules/regex-recursion/src/index.js generated vendored Normal file
View File

@@ -0,0 +1,365 @@
import {Context, forEachUnescaped, getGroupContents, hasUnescaped, replaceUnescaped} from 'regex-utilities';
const r = String.raw;
const gRToken = r`\\g<(?<gRNameOrNum>[^>&]+)&R=(?<gRDepth>[^>]+)>`;
const recursiveToken = r`\(\?R=(?<rDepth>[^\)]+)\)|${gRToken}`;
const namedCaptureDelim = r`\(\?<(?![=!])(?<captureName>[^>]+)>`;
const captureDelim = r`${namedCaptureDelim}|(?<unnamed>\()(?!\?)`;
const token = new RegExp(r`${namedCaptureDelim}|${recursiveToken}|\(\?|\\?.`, 'gsu');
const overlappingRecursionMsg = 'Cannot use multiple overlapping recursions';
/**
@param {string} pattern
@param {{
flags?: string;
captureTransfers?: Map<number, Array<number>>;
hiddenCaptures?: Array<number>;
mode?: 'plugin' | 'external';
}} [data]
@returns {{
pattern: string;
captureTransfers: Map<number, Array<number>>;
hiddenCaptures: Array<number>;
}}
*/
function recursion(pattern, data) {
const {hiddenCaptures, mode} = {
hiddenCaptures: [],
mode: 'plugin',
...data,
};
// Capture transfer is used by <github.com/slevithan/oniguruma-to-es>
let captureTransfers = data?.captureTransfers ?? new Map();
// Keep the initial fail-check (which avoids unneeded processing) as fast as possible by testing
// without the accuracy improvement of using `hasUnescaped` with `Context.DEFAULT`
if (!(new RegExp(recursiveToken, 'su').test(pattern))) {
return {
pattern,
captureTransfers,
hiddenCaptures,
};
}
if (mode === 'plugin' && hasUnescaped(pattern, r`\(\?\(DEFINE\)`, Context.DEFAULT)) {
throw new Error('DEFINE groups cannot be used with recursion');
}
const addedHiddenCaptures = [];
const hasNumberedBackref = hasUnescaped(pattern, r`\\[1-9]`, Context.DEFAULT);
const groupContentsStartPos = new Map();
const openGroups = [];
let hasRecursed = false;
let numCharClassesOpen = 0;
let numCapturesPassed = 0;
let match;
token.lastIndex = 0;
while ((match = token.exec(pattern))) {
const {0: m, groups: {captureName, rDepth, gRNameOrNum, gRDepth}} = match;
if (m === '[') {
numCharClassesOpen++;
} else if (!numCharClassesOpen) {
// `(?R=N)`
if (rDepth) {
assertMaxInBounds(rDepth);
if (hasRecursed) {
throw new Error(overlappingRecursionMsg);
}
if (hasNumberedBackref) {
// Could add support for numbered backrefs with extra effort, but it's probably not worth
// it. To trigger this error, the regex must include recursion and one of the following:
// - An interpolated regex that contains a numbered backref (since other numbered
// backrefs are prevented by implicit flag n).
// - A numbered backref, when flag n is explicitly disabled.
// Note that Regex+'s extended syntax (atomic groups and sometimes subroutines) can also
// add numbered backrefs, but those work fine because external plugins like this one run
// *before* the transformation of built-in syntax extensions
throw new Error(
// When used in `external` mode by transpilers other than Regex+, backrefs might have
// gone through conversion from named to numbered, so avoid a misleading error
`${mode === 'external' ? 'Backrefs' : 'Numbered backrefs'} cannot be used with global recursion`
);
}
const left = pattern.slice(0, match.index);
const right = pattern.slice(token.lastIndex);
if (hasUnescaped(right, recursiveToken, Context.DEFAULT)) {
throw new Error(overlappingRecursionMsg);
}
const reps = +rDepth - 1;
pattern = makeRecursive(
left,
right,
reps,
false,
hiddenCaptures,
addedHiddenCaptures,
numCapturesPassed
);
captureTransfers = mapCaptureTransfers(
captureTransfers,
left,
reps,
addedHiddenCaptures.length,
0,
numCapturesPassed
);
// No need to parse further
break;
// `\g<name&R=N>`, `\g<number&R=N>`
} else if (gRNameOrNum) {
assertMaxInBounds(gRDepth);
let isWithinReffedGroup = false;
for (const g of openGroups) {
if (g.name === gRNameOrNum || g.num === +gRNameOrNum) {
isWithinReffedGroup = true;
if (g.hasRecursedWithin) {
throw new Error(overlappingRecursionMsg);
}
break;
}
}
if (!isWithinReffedGroup) {
throw new Error(r`Recursive \g cannot be used outside the referenced group "${
mode === 'external' ? gRNameOrNum : r`\g<${gRNameOrNum}&R=${gRDepth}>`
}"`);
}
const startPos = groupContentsStartPos.get(gRNameOrNum);
const groupContents = getGroupContents(pattern, startPos);
if (
hasNumberedBackref &&
hasUnescaped(groupContents, r`${namedCaptureDelim}|\((?!\?)`, Context.DEFAULT)
) {
throw new Error(
// When used in `external` mode by transpilers other than Regex+, backrefs might have
// gone through conversion from named to numbered, so avoid a misleading error
`${mode === 'external' ? 'Backrefs' : 'Numbered backrefs'} cannot be used with recursion of capturing groups`
);
}
const groupContentsLeft = pattern.slice(startPos, match.index);
const groupContentsRight = groupContents.slice(groupContentsLeft.length + m.length);
const numAddedHiddenCapturesPreExpansion = addedHiddenCaptures.length;
const reps = +gRDepth - 1;
const expansion = makeRecursive(
groupContentsLeft,
groupContentsRight,
reps,
true,
hiddenCaptures,
addedHiddenCaptures,
numCapturesPassed
);
captureTransfers = mapCaptureTransfers(
captureTransfers,
groupContentsLeft,
reps,
addedHiddenCaptures.length - numAddedHiddenCapturesPreExpansion,
numAddedHiddenCapturesPreExpansion,
numCapturesPassed
);
const pre = pattern.slice(0, startPos);
const post = pattern.slice(startPos + groupContents.length);
// Modify the string we're looping over
pattern = `${pre}${expansion}${post}`;
// Step forward for the next loop iteration
token.lastIndex += expansion.length - m.length - groupContentsLeft.length - groupContentsRight.length;
openGroups.forEach(g => g.hasRecursedWithin = true);
hasRecursed = true;
} else if (captureName) {
numCapturesPassed++;
groupContentsStartPos.set(String(numCapturesPassed), token.lastIndex);
groupContentsStartPos.set(captureName, token.lastIndex);
openGroups.push({
num: numCapturesPassed,
name: captureName,
});
} else if (m[0] === '(') {
const isUnnamedCapture = m === '(';
if (isUnnamedCapture) {
numCapturesPassed++;
groupContentsStartPos.set(String(numCapturesPassed), token.lastIndex);
}
openGroups.push(isUnnamedCapture ? {num: numCapturesPassed} : {});
} else if (m === ')') {
openGroups.pop();
}
} else if (m === ']') {
numCharClassesOpen--;
}
}
hiddenCaptures.push(...addedHiddenCaptures);
return {
pattern,
captureTransfers,
hiddenCaptures,
};
}
/**
@param {string} max
*/
function assertMaxInBounds(max) {
const errMsg = `Max depth must be integer between 2 and 100; used ${max}`;
if (!/^[1-9]\d*$/.test(max)) {
throw new Error(errMsg);
}
max = +max;
if (max < 2 || max > 100) {
throw new Error(errMsg);
}
}
/**
@param {string} left
@param {string} right
@param {number} reps
@param {boolean} isSubpattern
@param {Array<number>} hiddenCaptures
@param {Array<number>} addedHiddenCaptures
@param {number} numCapturesPassed
@returns {string}
*/
function makeRecursive(
left,
right,
reps,
isSubpattern,
hiddenCaptures,
addedHiddenCaptures,
numCapturesPassed
) {
const namesInRecursed = new Set();
// Can skip this work if not needed
if (isSubpattern) {
forEachUnescaped(left + right, namedCaptureDelim, ({groups: {captureName}}) => {
namesInRecursed.add(captureName);
}, Context.DEFAULT);
}
const rest = [
reps,
isSubpattern ? namesInRecursed : null,
hiddenCaptures,
addedHiddenCaptures,
numCapturesPassed,
];
// Depth 2: 'left(?:left(?:)right)right'
// Depth 3: 'left(?:left(?:left(?:)right)right)right'
// Empty group in the middle separates tokens and absorbs a following quantifier if present
return `${left}${
repeatWithDepth(`(?:${left}`, 'forward', ...rest)
}(?:)${
repeatWithDepth(`${right})`, 'backward', ...rest)
}${right}`;
}
/**
@param {string} pattern
@param {'forward' | 'backward'} direction
@param {number} reps
@param {Set<string> | null} namesInRecursed
@param {Array<number>} hiddenCaptures
@param {Array<number>} addedHiddenCaptures
@param {number} numCapturesPassed
@returns {string}
*/
function repeatWithDepth(
pattern,
direction,
reps,
namesInRecursed,
hiddenCaptures,
addedHiddenCaptures,
numCapturesPassed
) {
const startNum = 2;
const getDepthNum = i => direction === 'forward' ? (i + startNum) : (reps - i + startNum - 1);
let result = '';
for (let i = 0; i < reps; i++) {
const depthNum = getDepthNum(i);
result += replaceUnescaped(
pattern,
r`${captureDelim}|\\k<(?<backref>[^>]+)>`,
({0: m, groups: {captureName, unnamed, backref}}) => {
if (backref && namesInRecursed && !namesInRecursed.has(backref)) {
// Don't alter backrefs to groups outside the recursed subpattern
return m;
}
const suffix = `_$${depthNum}`;
if (unnamed || captureName) {
const addedCaptureNum = numCapturesPassed + addedHiddenCaptures.length + 1;
addedHiddenCaptures.push(addedCaptureNum);
incrementIfAtLeast(hiddenCaptures, addedCaptureNum);
return unnamed ? m : `(?<${captureName}${suffix}>`;
}
return r`\k<${backref}${suffix}>`;
},
Context.DEFAULT
);
}
return result;
}
/**
Updates the array in place by incrementing each value greater than or equal to the threshold.
@param {Array<number>} arr
@param {number} threshold
*/
function incrementIfAtLeast(arr, threshold) {
for (let i = 0; i < arr.length; i++) {
if (arr[i] >= threshold) {
arr[i]++;
}
}
}
/**
@param {Map<number, Array<number>>} captureTransfers
@param {string} left
@param {number} reps
@param {number} numCapturesAddedInExpansion
@param {number} numAddedHiddenCapturesPreExpansion
@param {number} numCapturesPassed
@returns {Map<number, Array<number>>}
*/
function mapCaptureTransfers(captureTransfers, left, reps, numCapturesAddedInExpansion, numAddedHiddenCapturesPreExpansion, numCapturesPassed) {
if (captureTransfers.size && numCapturesAddedInExpansion) {
let numCapturesInLeft = 0;
forEachUnescaped(left, captureDelim, () => numCapturesInLeft++, Context.DEFAULT);
// Is 0 for global recursion
const recursionDelimCaptureNum = numCapturesPassed - numCapturesInLeft + numAddedHiddenCapturesPreExpansion;
const newCaptureTransfers = new Map();
captureTransfers.forEach((from, to) => {
const numCapturesInRight = (numCapturesAddedInExpansion - (numCapturesInLeft * reps)) / reps;
const numCapturesAddedInLeft = numCapturesInLeft * reps;
const newTo = to > (recursionDelimCaptureNum + numCapturesInLeft) ? to + numCapturesAddedInExpansion : to;
const newFrom = [];
for (const f of from) {
// Before the recursed subpattern
if (f <= recursionDelimCaptureNum) {
newFrom.push(f);
// After the recursed subpattern
} else if (f > (recursionDelimCaptureNum + numCapturesInLeft + numCapturesInRight)) {
newFrom.push(f + numCapturesAddedInExpansion);
// Within the recursed subpattern, on the left of the recursion token
} else if (f <= (recursionDelimCaptureNum + numCapturesInLeft)) {
for (let i = 0; i <= reps; i++) {
newFrom.push(f + (numCapturesInLeft * i));
}
// Within the recursed subpattern, on the right of the recursion token
} else {
for (let i = 0; i <= reps; i++) {
newFrom.push(f + numCapturesAddedInLeft + (numCapturesInRight * i));
}
}
}
newCaptureTransfers.set(newTo, newFrom);
});
return newCaptureTransfers;
}
return captureTransfers;
}
export {
recursion,
};

24
node_modules/regex-recursion/types/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,24 @@
/**
@param {string} pattern
@param {{
flags?: string;
captureTransfers?: Map<number, Array<number>>;
hiddenCaptures?: Array<number>;
mode?: 'plugin' | 'external';
}} [data]
@returns {{
pattern: string;
captureTransfers: Map<number, Array<number>>;
hiddenCaptures: Array<number>;
}}
*/
export function recursion(pattern: string, data?: {
flags?: string;
captureTransfers?: Map<number, Array<number>>;
hiddenCaptures?: Array<number>;
mode?: "plugin" | "external";
}): {
pattern: string;
captureTransfers: Map<number, Array<number>>;
hiddenCaptures: Array<number>;
};