regex-recursion
Advanced tools
Comparing version 4.3.0 to 5.0.0
@@ -1,2 +0,2 @@ | ||
var Regex;(Regex||={}).plugins=(()=>{var U=Object.defineProperty;var G=Object.getOwnPropertyDescriptor;var M=Object.getOwnPropertyNames;var W=Object.prototype.hasOwnProperty;var P=(e,t)=>{for(var n in t)U(e,n,{get:t[n],enumerable:!0})},_=(e,t,n,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of M(t))!W.call(e,o)&&o!==n&&U(e,o,{get:()=>t[o],enumerable:!(s=G(t,o))||s.enumerable});return e};var H=e=>_(U({},"__esModule",{value:!0}),e);var q={};P(q,{recursion:()=>z});var a=Object.freeze({DEFAULT:"DEFAULT",CHAR_CLASS:"CHAR_CLASS"});function A(e,t,n,s){let o=new RegExp(String.raw`${t}|(?<$skip>\[\^?|\\?.)`,"gsu"),c=[!1],r=0,u="";for(let i of e.matchAll(o)){let{0:l,groups:{$skip:p}}=i;if(!p&&(!s||s===a.DEFAULT==!r)){n instanceof Function?u+=n(i,{context:r?a.CHAR_CLASS:a.DEFAULT,negated:c[c.length-1]}):u+=n;continue}l[0]==="["?(r++,c.push(l[1]==="^")):l==="]"&&r&&(r--,c.pop()),u+=l}return u}function F(e,t,n,s){A(e,t,n,s)}function B(e,t,n=0,s){if(!new RegExp(t,"su").test(e))return null;let o=new RegExp(`${t}|(?<$skip>\\\\?.)`,"gsu");o.lastIndex=n;let c=0,r;for(;r=o.exec(e);){let{0:u,groups:{$skip:i}}=r;if(!i&&(!s||s===a.DEFAULT==!c))return r;u==="["?c++:u==="]"&&c&&c--,o.lastIndex==r.index&&o.lastIndex++}return null}function $(e,t,n){return!!B(e,t,0,n)}function L(e,t){let n=/\\?./gsu;n.lastIndex=t;let s=e.length,o=0,c=1,r;for(;r=n.exec(e);){let[u]=r;if(u==="[")o++;else if(o)u==="]"&&o--;else if(u==="(")c++;else if(u===")"&&(c--,!c)){s=r.index;break}}return e.slice(t,s)}var f=String.raw,j=f`\\g<(?<gRNameOrNum>[^>&]+)&R=(?<gRDepth>[^>]+)>`,x=f`\(\?R=(?<rDepth>[^\)]+)\)|${j}`,C=f`\(\?<(?![=!])(?<captureName>[^>]+)>`,w=new RegExp(f`${C}|${x}|\(\?|\\?.`,"gsu"),k="Cannot use multiple overlapping recursions";function z(e){if(!new RegExp(x,"su").test(e))return e;if($(e,f`\(\?\(DEFINE\)`,a.DEFAULT))throw new Error("DEFINE groups cannot be used with recursion");let t=$(e,f`\\[1-9]`,a.DEFAULT),n=new Map,s=[],o=!1,c=0,r=0,u;for(w.lastIndex=0;u=w.exec(e);){let{0:i,groups:{captureName:l,rDepth:p,gRNameOrNum:h,gRDepth:m}}=u;if(i==="[")c++;else if(c)i==="]"&&c--;else if(p){if(I(p),o)throw new Error(k);if(t)throw new Error("Numbered backrefs cannot be used with global recursion");let g=e.slice(0,u.index),d=e.slice(w.lastIndex);if($(d,x,a.DEFAULT))throw new Error(k);return T(g,d,+p,!1)}else if(h){I(m);let g=!1;for(let E of s)if(E.name===h||E.num===+h){if(g=!0,E.hasRecursedWithin)throw new Error(k);break}if(!g)throw new Error(f`Recursive \g cannot be used outside the referenced group "\g<${h}&R=${m}>"`);let d=n.get(h),R=L(e,d);if(t&&$(R,f`${C}|\((?!\?)`,a.DEFAULT))throw new Error("Numbered backrefs cannot be used with recursion of capturing groups");let D=e.slice(d,u.index),N=R.slice(D.length+i.length),b=T(D,N,+m,!0),O=e.slice(0,d),v=e.slice(d+R.length);e=`${O}${b}${v}`,w.lastIndex+=b.length-i.length-D.length-N.length,s.forEach(E=>E.hasRecursedWithin=!0),o=!0}else if(l)r++,n.set(String(r),w.lastIndex),n.set(l,w.lastIndex),s.push({num:r,name:l});else if(i.startsWith("(")){let g=i==="(";g&&(r++,n.set(String(r),w.lastIndex)),s.push(g?{num:r}:{})}else i===")"&&s.pop()}return e}function I(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 T(e,t,n,s){let o=new Set;s&&F(e+t,C,({groups:{captureName:r}})=>{o.add(r)},a.DEFAULT);let c=n-1;return`${e}${S(`(?:${e}`,c,s?o:null)}(?:)${S(`${t})`,c,s?o:null,"backward")}${t}`}function S(e,t,n,s="forward"){let c=u=>s==="backward"?t-u+2-1:u+2,r="";for(let u=0;u<t;u++){let i=c(u);r+=A(e,f`${C}|\\k<(?<backref>[^>]+)>`,({0:l,groups:{captureName:p,backref:h}})=>{if(h&&n&&!n.has(h))return l;let m=`_$${i}`;return p?`(?<${p}${m}>`:f`\k<${h}${m}>`},a.DEFAULT)}return r}return H(q);})(); | ||
var Regex;(Regex||={}).plugins=(()=>{var A=Object.defineProperty;var P=Object.getOwnPropertyDescriptor;var _=Object.getOwnPropertyNames;var H=Object.prototype.hasOwnProperty;var B=(e,t)=>{for(var r in t)A(e,r,{get:t[r],enumerable:!0})},j=(e,t,r,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of _(t))!H.call(e,n)&&n!==r&&A(e,n,{get:()=>t[n],enumerable:!(o=P(t,n))||o.enumerable});return e};var z=e=>j(A({},"__esModule",{value:!0}),e);var K={};B(K,{recursion:()=>J});var f=Object.freeze({DEFAULT:"DEFAULT",CHAR_CLASS:"CHAR_CLASS"});function x(e,t,r,o){let n=new RegExp(String.raw`${t}|(?<$skip>\[\^?|\\?.)`,"gsu"),s=[!1],u=0,c="";for(let i of e.matchAll(n)){let{0:l,groups:{$skip:a}}=i;if(!a&&(!o||o===f.DEFAULT==!u)){r instanceof Function?c+=r(i,{context:u?f.CHAR_CLASS:f.DEFAULT,negated:s[s.length-1]}):c+=r;continue}l[0]==="["?(u++,s.push(l[1]==="^")):l==="]"&&u&&(u--,s.pop()),c+=l}return c}function I(e,t,r,o){x(e,t,r,o)}function q(e,t,r=0,o){if(!new RegExp(t,"su").test(e))return null;let n=new RegExp(`${t}|(?<$skip>\\\\?.)`,"gsu");n.lastIndex=r;let s=0,u;for(;u=n.exec(e);){let{0:c,groups:{$skip:i}}=u;if(!i&&(!o||o===f.DEFAULT==!s))return u;c==="["?s++:c==="]"&&s&&s--,n.lastIndex==u.index&&n.lastIndex++}return null}function C(e,t,r){return!!q(e,t,0,r)}function T(e,t){let r=/\\?./gsu;r.lastIndex=t;let o=e.length,n=0,s=1,u;for(;u=r.exec(e);){let[c]=u;if(c==="[")n++;else if(n)c==="]"&&n--;else if(c==="(")s++;else if(c===")"&&(s--,!s)){o=u.index;break}}return e.slice(t,o)}var h=String.raw,y=h`\\g<(?<gRNameOrNum>[^>&]+)&R=(?<gRDepth>[^>]+)>`,b=h`\(\?R=(?<rDepth>[^\)]+)\)|${y}`,R=h`\(\?<(?![=!])(?<captureName>[^>]+)>`,$=new RegExp(h`${R}|${b}|\(\?|\\?.`,"gsu"),N="Cannot use multiple overlapping recursions",S="$E$";function J(e,t){if(!new RegExp(b,"su").test(e))return e;if(C(e,h`\(\?\(DEFINE\)`,f.DEFAULT))throw new Error("DEFINE groups cannot be used with recursion");let r=!!t?.useEmulationGroups,o=C(e,h`\\[1-9]`,f.DEFAULT),n=new Map,s=[],u=!1,c=0,i=0,l;for($.lastIndex=0;l=$.exec(e);){let{0:a,groups:{captureName:m,rDepth:g,gRNameOrNum:p,gRDepth:D}}=l;if(a==="[")c++;else if(c)a==="]"&&c--;else if(g){if(O(g),u)throw new Error(N);if(o)throw new Error("Numbered backrefs cannot be used with global recursion");let d=e.slice(0,l.index),w=e.slice($.lastIndex);if(C(w,b,f.DEFAULT))throw new Error(N);return G(d,w,+g,!1,r)}else if(p){O(D);let d=!1;for(let E of s)if(E.name===p||E.num===+p){if(d=!0,E.hasRecursedWithin)throw new Error(N);break}if(!d)throw new Error(h`Recursive \g cannot be used outside the referenced group "\g<${p}&R=${D}>"`);let w=n.get(p),U=T(e,w);if(o&&C(U,h`${R}|\((?!\?)`,f.DEFAULT))throw new Error("Numbered backrefs cannot be used with recursion of capturing groups");let k=e.slice(w,l.index),F=U.slice(k.length+a.length),L=G(k,F,+D,!0,r),v=e.slice(0,w),W=e.slice(w+U.length);e=`${v}${L}${W}`,$.lastIndex+=L.length-a.length-k.length-F.length,s.forEach(E=>E.hasRecursedWithin=!0),u=!0}else if(m)i++,n.set(String(i),$.lastIndex),n.set(m,$.lastIndex),s.push({num:i,name:m});else if(a.startsWith("(")){let d=a==="(";d&&(i++,n.set(String(i),$.lastIndex)),s.push(d?{num:i}:{})}else a===")"&&s.pop()}return e}function O(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 G(e,t,r,o,n){let s=new Set;o&&I(e+t,R,({groups:{captureName:c}})=>{s.add(c)},f.DEFAULT);let u=r-1;return`${e}${M(`(?:${e}`,u,o?s:null,"forward",n)}(?:)${M(`${t})`,u,o?s:null,"backward",n)}${t}`}function M(e,t,r,o,n){let u=i=>o==="backward"?t-i+2-1:i+2,c="";for(let i=0;i<t;i++){let l=u(i);c+=x(e,h`${R}|\\k<(?<backref>[^>]+)>${n?h`|\((?!\?)`:""}`,({0:a,groups:{captureName:m,backref:g}})=>{if(g&&r&&!r.has(g))return a;if(a==="(")return`(${S}`;let p=`_$${l}`;return m?`(?<${m}${p}>${n?S:""}`:h`\k<${g}${p}>`},f.DEFAULT)}return c}return z(K);})(); | ||
//# sourceMappingURL=regex-recursion.min.js.map |
{ | ||
"name": "regex-recursion", | ||
"version": "4.3.0", | ||
"version": "5.0.0", | ||
"description": "Recursive matching plugin for Regex+", | ||
@@ -16,2 +16,11 @@ "author": "Steven Levithan", | ||
"types": "./types/index.d.ts", | ||
"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", | ||
"prepare": "pnpm test" | ||
}, | ||
"files": [ | ||
@@ -36,14 +45,6 @@ "dist", | ||
"esbuild": "^0.24.0", | ||
"jasmine": "^5.4.0", | ||
"jasmine": "^5.5.0", | ||
"regex": "^5.0.2", | ||
"typescript": "^5.7.2" | ||
}, | ||
"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" | ||
} | ||
} | ||
} |
@@ -34,3 +34,3 @@ # regex-recursion | ||
<script src="https://cdn.jsdelivr.net/npm/regex@5.0.2/dist/regex.min.js"></script> | ||
<script src="https://cdn.jsdelivr.net/npm/regex-recursion@4.3.0/dist/regex-recursion.min.js"></script> | ||
<script src="https://cdn.jsdelivr.net/npm/regex-recursion@5.0.0/dist/regex-recursion.min.js"></script> | ||
<script> | ||
@@ -37,0 +37,0 @@ const {regex} = Regex; |
@@ -9,8 +9,14 @@ import {Context, forEachUnescaped, getGroupContents, hasUnescaped, replaceUnescaped} from 'regex-utilities'; | ||
const overlappingRecursionMsg = 'Cannot use multiple overlapping recursions'; | ||
// See <github.com/slevithan/regex/blob/main/src/subclass.js> | ||
const emulationGroupMarker = '$E$'; | ||
/** | ||
@param {string} expression | ||
@param {{ | ||
flags?: string; | ||
useEmulationGroups?: boolean; | ||
}} [data] | ||
@returns {string} | ||
*/ | ||
export function recursion(expression) { | ||
export function recursion(expression, data) { | ||
// Keep the initial fail-check (which avoids unneeded processing) as fast as possible by testing | ||
@@ -24,2 +30,3 @@ // without the accuracy improvement of using `hasUnescaped` with default `Context` | ||
} | ||
const useEmulationGroups = !!data?.useEmulationGroups; | ||
const hasNumberedBackref = hasUnescaped(expression, r`\\[1-9]`, Context.DEFAULT); | ||
@@ -62,3 +69,3 @@ const groupContentsStartPos = new Map(); | ||
// No need to parse further | ||
return makeRecursive(pre, post, +rDepth, false); | ||
return makeRecursive(pre, post, +rDepth, false, useEmulationGroups); | ||
// `\g<name&R=N>`, `\g<number&R=N>` | ||
@@ -90,3 +97,3 @@ } else if (gRNameOrNum) { | ||
const groupContentsPost = groupContents.slice(groupContentsPre.length + m.length); | ||
const expansion = makeRecursive(groupContentsPre, groupContentsPost, +gRDepth, true); | ||
const expansion = makeRecursive(groupContentsPre, groupContentsPost, +gRDepth, true, useEmulationGroups); | ||
const pre = expression.slice(0, startPos); | ||
@@ -146,5 +153,6 @@ const post = expression.slice(startPos + groupContents.length); | ||
@param {boolean} isSubpattern | ||
@param {boolean} useEmulationGroups | ||
@returns {string} | ||
*/ | ||
function makeRecursive(pre, post, maxDepth, isSubpattern) { | ||
function makeRecursive(pre, post, maxDepth, isSubpattern, useEmulationGroups) { | ||
const namesInRecursed = new Set(); | ||
@@ -161,5 +169,5 @@ // Avoid this work if not needed | ||
return `${pre}${ | ||
repeatWithDepth(`(?:${pre}`, reps, (isSubpattern ? namesInRecursed : null)) | ||
repeatWithDepth(`(?:${pre}`, reps, (isSubpattern ? namesInRecursed : null), 'forward', useEmulationGroups) | ||
}(?:)${ | ||
repeatWithDepth(`${post})`, reps, (isSubpattern ? namesInRecursed : null), 'backward') | ||
repeatWithDepth(`${post})`, reps, (isSubpattern ? namesInRecursed : null), 'backward', useEmulationGroups) | ||
}${post}`; | ||
@@ -172,6 +180,7 @@ } | ||
@param {Set<string> | null} namesInRecursed | ||
@param {'forward' | 'backward'} [direction] | ||
@param {'forward' | 'backward'} direction | ||
@param {boolean} useEmulationGroups | ||
@returns {string} | ||
*/ | ||
function repeatWithDepth(expression, reps, namesInRecursed, direction = 'forward') { | ||
function repeatWithDepth(expression, reps, namesInRecursed, direction, useEmulationGroups) { | ||
const startNum = 2; | ||
@@ -184,3 +193,3 @@ const depthNum = i => direction === 'backward' ? reps - i + startNum - 1 : i + startNum; | ||
expression, | ||
r`${namedCapturingDelim}|\\k<(?<backref>[^>]+)>`, | ||
r`${namedCapturingDelim}|\\k<(?<backref>[^>]+)>${useEmulationGroups ? r`|\((?!\?)` : ''}`, | ||
({0: m, groups: {captureName, backref}}) => { | ||
@@ -191,4 +200,9 @@ if (backref && namesInRecursed && !namesInRecursed.has(backref)) { | ||
} | ||
if (m === '(') { | ||
return `(${emulationGroupMarker}`; | ||
} | ||
const suffix = `_$${captureNum}`; | ||
return captureName ? `(?<${captureName}${suffix}>` : r`\k<${backref}${suffix}>`; | ||
return captureName ? | ||
`(?<${captureName}${suffix}>${useEmulationGroups ? emulationGroupMarker : ''}` : | ||
r`\k<${backref}${suffix}>`; | ||
}, | ||
@@ -195,0 +209,0 @@ Context.DEFAULT |
/** | ||
@param {string} expression | ||
@param {{ | ||
flags?: string; | ||
useEmulationGroups?: boolean; | ||
}} [data] | ||
@returns {string} | ||
*/ | ||
export function recursion(expression: string): string; | ||
export function recursion(expression: string, data?: { | ||
flags?: string; | ||
useEmulationGroups?: boolean; | ||
}): string; |
Sorry, the diff of this file is not supported yet
40121
224