regex-recursion
Advanced tools
Comparing version 4.2.1 to 4.3.0
@@ -1,2 +0,2 @@ | ||
var Regex;(Regex||={}).plugins=(()=>{var $=Object.defineProperty;var O=Object.getOwnPropertyDescriptor;var v=Object.getOwnPropertyNames;var G=Object.prototype.hasOwnProperty;var M=(e,t)=>{for(var n in t)$(e,n,{get:t[n],enumerable:!0})},W=(e,t,n,c)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of v(t))!G.call(e,s)&&s!==n&&$(e,s,{get:()=>t[s],enumerable:!(c=O(t,s))||c.enumerable});return e};var P=e=>W($({},"__esModule",{value:!0}),e);var z={};M(z,{recursion:()=>j});var g=Object.freeze({DEFAULT:"DEFAULT",CHAR_CLASS:"CHAR_CLASS"});function C(e,t,n,c){let s=new RegExp(String.raw`${t}|(?<$skip>\[\^?|\\?.)`,"gsu"),o=[!1],u=0,r="";for(let i of e.matchAll(s)){let{0:a,groups:{$skip:l}}=i;if(!l&&(!c||c===g.DEFAULT==!u)){n instanceof Function?r+=n(i,{context:u?g.CHAR_CLASS:g.DEFAULT,negated:o[o.length-1]}):r+=n;continue}a[0]==="["?(u++,o.push(a[1]==="^")):a==="]"&&u&&(u--,o.pop()),r+=a}return r}function S(e,t,n,c){C(e,t,n,c)}function _(e,t,n=0,c){if(!new RegExp(t,"su").test(e))return null;let s=new RegExp(`${t}|(?<$skip>\\\\?.)`,"gsu");s.lastIndex=n;let o=0,u;for(;u=s.exec(e);){let{0:r,groups:{$skip:i}}=u;if(!i&&(!c||c===g.DEFAULT==!o))return u;r==="["?o++:r==="]"&&o&&o--,s.lastIndex==u.index&&s.lastIndex++}return null}function m(e,t,n){return!!_(e,t,0,n)}function F(e,t){let n=/\\?./gsu;n.lastIndex=t;let c=e.length,s=0,o=1,u;for(;u=n.exec(e);){let[r]=u;if(r==="[")s++;else if(s)r==="]"&&s--;else if(r==="(")o++;else if(r===")"&&(o--,!o)){c=u.index;break}}return e.slice(t,c)}var H=String.raw`\\g<(?<gRNameOrNum>[^>&]+)&R=(?<gRDepth>[^>]+)>`,D=String.raw`\(\?R=(?<rDepth>[^\)]+)\)|${H}`,U=String.raw`\(\?<(?![=!])(?<captureName>[^>]+)>`,d=new RegExp(String.raw`${U}|${D}|\(\?|\\?.`,"gsu"),R="Cannot use multiple overlapping recursions";function j(e){if(!new RegExp(D,"su").test(e))return e;if(m(e,String.raw`\\[1-9]`,g.DEFAULT))throw new Error("Numbered backrefs cannot be used with recursion");if(m(e,String.raw`\(\?\(DEFINE\)`,g.DEFAULT))throw new Error("DEFINE groups cannot be used with recursion");let t=new Map,n=[],c=!1,s=0,o=0,u;for(d.lastIndex=0;u=d.exec(e);){let{0:r,groups:{captureName:i,rDepth:a,gRNameOrNum:l,gRDepth:h}}=u;if(r==="[")s++;else if(s)r==="]"&&s--;else if(a){if(I(a),c)throw new Error(R);let f=e.slice(0,u.index),p=e.slice(d.lastIndex);if(m(p,D,g.DEFAULT))throw new Error(R);return L(f,p,+a,!1)}else if(l){I(h);let f=!1;for(let w of n)if(w.name===l||w.num===+l){if(f=!0,w.hasRecursedWithin)throw new Error(R);break}if(!f)throw new Error(`Recursive \\g cannot be used outside the referenced group "\\g<${l}&R=${h}>"`);let p=t.get(l),A=F(e,p),E=e.slice(p,u.index),k=A.slice(E.length+r.length),x=L(E,k,+h,!0),T=e.slice(0,p),b=e.slice(p+A.length);e=`${T}${x}${b}`,d.lastIndex+=x.length-r.length-E.length-k.length,n.forEach(w=>w.hasRecursedWithin=!0),c=!0}else if(i)o++,t.set(String(o),d.lastIndex),t.set(i,d.lastIndex),n.push({num:o,name:i});else if(r.startsWith("(")){let f=r==="(";f&&(o++,t.set(String(o),d.lastIndex)),n.push(f?{num:o}:{})}else r===")"&&n.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 L(e,t,n,c){let s=new Set;c&&S(e+t,U,({groups:{captureName:u}})=>{s.add(u)},g.DEFAULT);let o=n-1;return`${e}${N(`(?:${e}`,o,c?s:null)}(?:)${N(`${t})`,o,c?s:null,"backward")}${t}`}function N(e,t,n,c="forward"){let o=r=>c==="backward"?t-r+2-1:r+2,u="";for(let r=0;r<t;r++){let i=o(r);u+=C(e,String.raw`${U}|\\k<(?<backref>[^>]+)>`,({0:a,groups:{captureName:l,backref:h}})=>{if(h&&n&&!n.has(h))return a;let f=`_$${i}`;return l?`(?<${l}${f}>`:`\\k<${h}${f}>`},g.DEFAULT)}return u}return P(z);})(); | ||
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);})(); | ||
//# sourceMappingURL=regex-recursion.min.js.map |
{ | ||
"name": "regex-recursion", | ||
"version": "4.2.1", | ||
"version": "4.3.0", | ||
"description": "Recursive matching plugin for Regex+", | ||
@@ -16,11 +16,2 @@ "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": "npm run bundle:global && npm run types", | ||
"pretest": "npm run build", | ||
"test": "jasmine", | ||
"prepare": "npm test" | ||
}, | ||
"files": [ | ||
@@ -46,5 +37,13 @@ "dist", | ||
"jasmine": "^5.4.0", | ||
"regex": "^4.4.0", | ||
"typescript": "^5.6.3" | ||
"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" | ||
} | ||
} | ||
} |
@@ -6,3 +6,3 @@ # regex-recursion | ||
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, and support all regular expression features except numbered backreferences (support could be added in future versions). | ||
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. | ||
@@ -34,4 +34,4 @@ Recursive matching is added to a regex via one of the following (the recursion depth limit is provided in place of *`N`*): | ||
```html | ||
<script src="https://cdn.jsdelivr.net/npm/regex@4.4.0/dist/regex.min.js"></script> | ||
<script src="https://cdn.jsdelivr.net/npm/regex-recursion@4.2.1/dist/regex-recursion.min.js"></script> | ||
<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> | ||
@@ -38,0 +38,0 @@ const {regex} = Regex; |
import {Context, forEachUnescaped, getGroupContents, hasUnescaped, replaceUnescaped} from 'regex-utilities'; | ||
const gRToken = String.raw`\\g<(?<gRNameOrNum>[^>&]+)&R=(?<gRDepth>[^>]+)>`; | ||
const recursiveToken = String.raw`\(\?R=(?<rDepth>[^\)]+)\)|${gRToken}`; | ||
const namedCapturingDelim = String.raw`\(\?<(?![=!])(?<captureName>[^>]+)>`; | ||
const token = new RegExp(String.raw`${namedCapturingDelim}|${recursiveToken}|\(\?|\\?.`, 'gsu'); | ||
const r = String.raw; | ||
const gRToken = r`\\g<(?<gRNameOrNum>[^>&]+)&R=(?<gRDepth>[^>]+)>`; | ||
const recursiveToken = r`\(\?R=(?<rDepth>[^\)]+)\)|${gRToken}`; | ||
const namedCapturingDelim = r`\(\?<(?![=!])(?<captureName>[^>]+)>`; | ||
const token = new RegExp(r`${namedCapturingDelim}|${recursiveToken}|\(\?|\\?.`, 'gsu'); | ||
const overlappingRecursionMsg = 'Cannot use multiple overlapping recursions'; | ||
@@ -19,20 +20,6 @@ | ||
} | ||
if (hasUnescaped(expression, String.raw`\\[1-9]`, Context.DEFAULT)) { | ||
// 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 transpilation of built-in syntax extensions. | ||
// To support numbered backrefs, they would need to be automatically adjusted when they're | ||
// duplicated by recursion and refer to a group inside the expression being recursed. | ||
// Additionally, numbered backrefs inside and outside of the recursed expression would need to | ||
// be adjusted based on any capturing groups added by recursion. | ||
throw new Error(`Numbered backrefs cannot be used with recursion`); | ||
if (hasUnescaped(expression, r`\(\?\(DEFINE\)`, Context.DEFAULT)) { | ||
throw new Error('DEFINE groups cannot be used with recursion'); | ||
} | ||
if (hasUnescaped(expression, String.raw`\(\?\(DEFINE\)`, Context.DEFAULT)) { | ||
throw new Error(`DEFINE groups cannot be used with recursion`); | ||
} | ||
const hasNumberedBackref = hasUnescaped(expression, r`\\[1-9]`, Context.DEFAULT); | ||
const groupContentsStartPos = new Map(); | ||
@@ -57,2 +44,13 @@ const openGroups = []; | ||
} | ||
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('Numbered backrefs cannot be used with global recursion'); | ||
} | ||
const pre = expression.slice(0, match.index); | ||
@@ -79,6 +77,12 @@ const post = expression.slice(token.lastIndex); | ||
if (!isWithinReffedGroup) { | ||
throw new Error(`Recursive \\g cannot be used outside the referenced group "\\g<${gRNameOrNum}&R=${gRDepth}>"`); | ||
throw new Error(r`Recursive \g cannot be used outside the referenced group "\g<${gRNameOrNum}&R=${gRDepth}>"`); | ||
} | ||
const startPos = groupContentsStartPos.get(gRNameOrNum); | ||
const groupContents = getGroupContents(expression, startPos); | ||
if ( | ||
hasNumberedBackref && | ||
hasUnescaped(groupContents, r`${namedCapturingDelim}|\((?!\?)`, Context.DEFAULT) | ||
) { | ||
throw new Error('Numbered backrefs cannot be used with recursion of capturing groups'); | ||
} | ||
const groupContentsPre = expression.slice(startPos, match.index); | ||
@@ -176,3 +180,3 @@ const groupContentsPost = groupContents.slice(groupContentsPre.length + m.length); | ||
expression, | ||
String.raw`${namedCapturingDelim}|\\k<(?<backref>[^>]+)>`, | ||
r`${namedCapturingDelim}|\\k<(?<backref>[^>]+)>`, | ||
({0: m, groups: {captureName, backref}}) => { | ||
@@ -184,3 +188,3 @@ if (backref && namesInRecursed && !namesInRecursed.has(backref)) { | ||
const suffix = `_$${captureNum}`; | ||
return captureName ? `(?<${captureName}${suffix}>` : `\\k<${backref}${suffix}>`; | ||
return captureName ? `(?<${captureName}${suffix}>` : r`\k<${backref}${suffix}>`; | ||
}, | ||
@@ -187,0 +191,0 @@ Context.DEFAULT |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
38386
383895
203