@@ -42,7 +42,7 @@ export function recursion(expression) {
4242 // duplicated by recursion and refer to a group inside the expression being recursed.
4343 // Additionally, numbered backrefs inside and outside of the recursed expression would need to
4444 // be adjusted based on any capturing groups added by recursion.
45- throw new Error ( `Numbered backrefs cannot be used with recursion; use named backref instead ` ) ;
45+ throw new Error ( `Numbered backrefs cannot be used with recursion; use named backref` ) ;
4646 }
4747 if ( hasUnescaped ( expression , String . raw `\(\?\(DEFINE\)` , Context . DEFAULT ) ) {
4848 throw new Error ( `DEFINE groups cannot be used with recursion` ) ;
@@ -66,7 +66,7 @@ export function recursion(expression) {
6666 const pre = expression . slice ( 0 , match . index ) ;
6767 const post = expression . slice ( token . lastIndex ) ;
6868 assertNoFollowingRecursion ( post ) ;
69- return makeRecursive ( pre , post , maxDepth ) ;
69+ return makeRecursive ( pre , post , maxDepth , false ) ;
7070 // \g<name&R=N>
7171 } else if ( gRName ) {
7272 const maxDepth = + gRDepth ;
@@ -86,7 +86,7 @@ export function recursion(expression) {
8686 const post = recursiveGroupContents . slice ( pre . length + m . length ) ;
8787 assertNoFollowingRecursion ( post ) ;
8888 return expression . slice ( 0 , startPos ) +
89- makeRecursive ( pre , post , maxDepth ) +
89+ makeRecursive ( pre , post , maxDepth , true ) +
9090 expression . slice ( startPos + recursiveGroupContents . length ) ;
9191 }
9292
@@ -116,27 +116,31 @@ function assertNoFollowingRecursion(remainingExpression) {
116116@param {string } pre
117117@param {string } post
118118@param {number } maxDepth
119+ @param {boolean } isSubpattern
119120@returns {string }
120121*/
121- function makeRecursive ( pre , post , maxDepth ) {
122+ function makeRecursive ( pre , post , maxDepth , isSubpattern ) {
122123 const namesInRecursed = new Set ( ) ;
123- forEachUnescaped ( pre + post , namedCapturingDelim , ( { groups : { captureName} } ) => {
124- namesInRecursed . add ( captureName ) ;
125- } , Context . DEFAULT ) ;
124+ // Avoid this work if not needed
125+ if ( isSubpattern ) {
126+ forEachUnescaped ( pre + post , namedCapturingDelim , ( { groups : { captureName} } ) => {
127+ namesInRecursed . add ( captureName ) ;
128+ } , Context . DEFAULT ) ;
129+ }
126130 const reps = maxDepth - 1 ;
127131 // Depth 2: 'pre(?:pre(?:)post)post'
128132 // Depth 3: 'pre(?:pre(?:pre(?:)post)post)post'
129133 return `${ pre } ${
130- repeatWithDepth ( `(?:${ pre } ` , reps , namesInRecursed )
134+ repeatWithDepth ( `(?:${ pre } ` , reps , isSubpattern ? namesInRecursed : null )
131135 } (?:)${
132- repeatWithDepth ( `${ post } )` , reps , namesInRecursed , 'backward' )
136+ repeatWithDepth ( `${ post } )` , reps , isSubpattern ? namesInRecursed : null , 'backward' )
133137 } ${ post } `;
134138}
135139
136140/**
137141@param {string } expression
138142@param {number } reps
139- @param {Set<string> } namesInRecursed
143+ @param {Set<string> | null } namesInRecursed
140144@param {'forward' | 'backward' } [direction]
141145@returns {string }
142146*/
@@ -150,7 +154,7 @@ function repeatWithDepth(expression, reps, namesInRecursed, direction = 'forward
150154 expression ,
151155 String . raw `${ namedCapturingDelim } |\\k<(?<backref>[^>]+)>` ,
152156 ( { 0 : m , groups : { captureName, backref} } ) => {
153- if ( backref && ! namesInRecursed . has ( backref ) ) {
157+ if ( backref && namesInRecursed && ! namesInRecursed . has ( backref ) ) {
154158 return m ;
155159 }
156160 const suffix = `_$${ captureNum } ` ;
0 commit comments