@@ -173,7 +173,7 @@ describe('recursion', () => {
173173 } ) ;
174174 } ) ;
175175
176- it ( 'should transfer to capture preceding recursion' , ( ) => {
176+ it ( 'should transfer to capture that precedes the recursion' , ( ) => {
177177 expect ( recursion ( r `()(()(a)()\g<2&R=2>?b)` , {
178178 captureTransfers : new Map ( [ [ 1 , 4 ] ] ) ,
179179 hiddenCaptures : [ 4 ] ,
@@ -188,7 +188,7 @@ describe('recursion', () => {
188188 } ) ) . toEqual ( {
189189 pattern : '()(a(?:a(?:)?()(b)())?()(b)())' ,
190190 captureTransfers : new Map ( [ [ 1 , 7 ] ] ) ,
191- hiddenCaptures : [ 7 , 3 , 4 , 5 ] ,
191+ hiddenCaptures : [ 7 , 3 , 4 , 5 ] , // unsorted
192192 } ) ;
193193 } ) ;
194194
@@ -203,30 +203,51 @@ describe('recursion', () => {
203203 } ) ;
204204
205205 it ( 'should transfer across multiple recursions' , ( ) => {
206+ // Capture in left contents of recursions
206207 expect ( recursion ( r `(?<r>(a)\g<r&R=2>?b) ((a)\g<3&R=2>?b)` , {
207208 captureTransfers : new Map ( [ [ 1 , 3 ] , [ 'r' , 3 ] , [ 2 , 4 ] ] ) ,
208209 } ) ) . toEqual ( {
209210 pattern : '(?<r>(a)(?:(a)(?:)?b)?b) ((a)(?:(a)(?:)?b)?b)' ,
210211 captureTransfers : new Map ( [ [ 1 , 4 ] , [ 'r' , 4 ] , [ 2 , 6 ] ] ) ,
211212 hiddenCaptures : [ 3 , 6 ] ,
212213 } ) ;
214+ // Capture in right contents of recursions
213215 expect ( recursion ( r `(?<r>a\g<r&R=2>?(b)) (a\g<3&R=2>?(b))` , {
214216 captureTransfers : new Map ( [ [ 1 , 3 ] , [ 'r' , 3 ] , [ 2 , 4 ] ] ) ,
215217 } ) ) . toEqual ( {
216218 pattern : '(?<r>a(?:a(?:)?(b))?(b)) (a(?:a(?:)?(b))?(b))' ,
217219 captureTransfers : new Map ( [ [ 1 , 4 ] , [ 'r' , 4 ] , [ 3 , 6 ] ] ) ,
218220 hiddenCaptures : [ 2 , 5 ] ,
219221 } ) ;
222+ // Capture in left and right contents of recursions
220223 expect ( recursion ( r `(?<r>(a)\g<r&R=2>?(b)) ((a)\g<4&R=2>?(b))` , {
221224 captureTransfers : new Map ( [ [ 1 , 4 ] , [ 'r' , 4 ] , [ 2 , 5 ] , [ 3 , 6 ] ] ) ,
222225 } ) ) . toEqual ( {
223226 pattern : '(?<r>(a)(?:(a)(?:)?(b))?(b)) ((a)(?:(a)(?:)?(b))?(b))' ,
224227 captureTransfers : new Map ( [ [ 1 , 6 ] , [ 'r' , 6 ] , [ 2 , 8 ] , [ 5 , 10 ] ] ) ,
225228 hiddenCaptures : [ 3 , 4 , 8 , 9 ] ,
226229 } ) ;
230+ // Triple recursion with capture transfer to middle (Oniguruma: `\g<a> (?<a>a\g<b>?b) (?<b>c\g<a>?d)`)
231+ expect ( recursion ( r `(a(c\g<1&R=2>?d)?b) (?<a>a(c\g<3&R=2>?d)?b) (?<b>c(a\g<5&R=2>?b)?d)` , {
232+ captureTransfers : new Map ( [ [ 3 , 6 ] , [ 'a' , 6 ] ] ) ,
233+ hiddenCaptures : [ 1 , 2 , 4 , 6 ] ,
234+ } ) ) . toEqual ( {
235+ pattern : '(a(c(?:a(c(?:)?d)?b)?d)?b) (?<a>a(c(?:a(c(?:)?d)?b)?d)?b) (?<b>c(a(?:c(a(?:)?b)?d)?b)?d)' ,
236+ captureTransfers : new Map ( [ [ 4 , 9 ] , [ 'a' , 9 ] ] ) ,
237+ hiddenCaptures : [ 1 , 2 , 5 , 8 , 3 , 6 , 9 ] , // unsorted
238+ } ) ;
239+ // Same as above but with depth 3
240+ expect ( recursion ( r `(a(c\g<1&R=3>?d)?b) (?<a>a(c\g<3&R=3>?d)?b) (?<b>c(a\g<5&R=3>?b)?d)` , {
241+ captureTransfers : new Map ( [ [ 3 , 6 ] , [ 'a' , 6 ] ] ) ,
242+ hiddenCaptures : [ 1 , 2 , 4 , 6 ] ,
243+ } ) ) . toEqual ( {
244+ pattern : '(a(c(?:a(c(?:a(c(?:)?d)?b)?d)?b)?d)?b) (?<a>a(c(?:a(c(?:a(c(?:)?d)?b)?d)?b)?d)?b) (?<b>c(a(?:c(a(?:c(a(?:)?b)?d)?b)?d)?b)?d)' ,
245+ captureTransfers : new Map ( [ [ 5 , 12 ] , [ 'a' , 12 ] ] ) ,
246+ hiddenCaptures : [ 1 , 2 , 6 , 10 , 3 , 4 , 7 , 8 , 11 , 12 ] , // unsorted
247+ } ) ;
227248 } ) ;
228249
229- it ( 'should transfer for captures after recursion' , ( ) => {
250+ it ( 'should transfer between captures following recursion' , ( ) => {
230251 expect ( recursion ( r `((2)\g<1&R=2>?) (3) (4)` , {
231252 captureTransfers : new Map ( [ [ 3 , 4 ] ] ) ,
232253 } ) ) . toEqual ( {
@@ -237,64 +258,64 @@ describe('recursion', () => {
237258 } ) ;
238259 } ) ;
239260 } ) ;
261+ } ) ;
240262
241- describe ( 'readme examples' , ( ) => {
242- it ( 'should match an equal number of two different subpatterns' , ( ) => {
243- const re = regex ( { plugins : [ recursion ] } ) `a(?R=20)?b` ;
244- expect ( re . exec ( 'test aaaaaabbb' ) [ 0 ] ) . toBe ( 'aaabbb' ) ;
245- } ) ;
263+ describe ( 'readme examples' , ( ) => {
264+ it ( 'should match an equal number of two different subpatterns' , ( ) => {
265+ const re = regex ( { plugins : [ recursion ] } ) `a(?R=20)?b` ;
266+ expect ( re . exec ( 'test aaaaaabbb' ) [ 0 ] ) . toBe ( 'aaabbb' ) ;
267+ } ) ;
246268
247- it ( 'should match an equal number of two different subpatterns, as the entire string' , ( ) => {
248- const re = regex ( { plugins : [ recursion ] } ) `
249- ^ (?<r> a \g<r&R=20>? b) $
250- ` ;
251- expect ( re . test ( 'aaabbb' ) ) . toBeTrue ( ) ;
252- expect ( re . test ( 'aaabb' ) ) . toBeFalse ( ) ;
253- } ) ;
269+ it ( 'should match an equal number of two different subpatterns, as the entire string' , ( ) => {
270+ const re = regex ( { plugins : [ recursion ] } ) `
271+ ^ (?<r> a \g<r&R=20>? b) $
272+ ` ;
273+ expect ( re . test ( 'aaabbb' ) ) . toBeTrue ( ) ;
274+ expect ( re . test ( 'aaabb' ) ) . toBeFalse ( ) ;
275+ } ) ;
254276
255- it ( 'should match balanced parentheses' , ( ) => {
256- const parens = regex ( { flags : 'g' , plugins : [ recursion ] } ) `
257- \( ([^\(\)] | (?R=20))* \)
258- ` ;
259- expect ( 'test ) (balanced ((parens))) () ((a)) ( (b)' . match ( parens ) ) . toEqual ( [ '(balanced ((parens)))' , '()' , '((a))' , '(b)' ] ) ;
260- } ) ;
277+ it ( 'should match balanced parentheses' , ( ) => {
278+ const parens = regex ( { flags : 'g' , plugins : [ recursion ] } ) `
279+ \( ([^\(\)] | (?R=20))* \)
280+ ` ;
281+ expect ( 'test ) (balanced ((parens))) () ((a)) ( (b)' . match ( parens ) ) . toEqual ( [ '(balanced ((parens)))' , '()' , '((a))' , '(b)' ] ) ;
282+ } ) ;
261283
262- it ( 'should match balanced parentheses using an atomic group' , ( ) => {
263- const parens = regex ( { flags : 'g' , plugins : [ recursion ] } ) `
264- \( ((?> [^\(\)]+) | (?R=20))* \)
265- ` ;
266- expect ( 'test ) (balanced ((parens))) () ((a)) ( (b)' . match ( parens ) ) . toEqual ( [ '(balanced ((parens)))' , '()' , '((a))' , '(b)' ] ) ;
267- } ) ;
284+ it ( 'should match balanced parentheses using an atomic group' , ( ) => {
285+ const parens = regex ( { flags : 'g' , plugins : [ recursion ] } ) `
286+ \( ((?> [^\(\)]+) | (?R=20))* \)
287+ ` ;
288+ expect ( 'test ) (balanced ((parens))) () ((a)) ( (b)' . match ( parens ) ) . toEqual ( [ '(balanced ((parens)))' , '()' , '((a))' , '(b)' ] ) ;
289+ } ) ;
268290
269- it ( 'should match balanced parentheses using a possessive quantifier' , ( ) => {
270- const parens = regex ( { flags : 'g' , plugins : [ recursion ] } ) `
271- \( ([^\(\)]++ | (?R=20))* \)
272- ` ;
273- expect ( 'test ) (balanced ((parens))) () ((a)) ( (b)' . match ( parens ) ) . toEqual ( [ '(balanced ((parens)))' , '()' , '((a))' , '(b)' ] ) ;
274- } ) ;
291+ it ( 'should match balanced parentheses using a possessive quantifier' , ( ) => {
292+ const parens = regex ( { flags : 'g' , plugins : [ recursion ] } ) `
293+ \( ([^\(\)]++ | (?R=20))* \)
294+ ` ;
295+ expect ( 'test ) (balanced ((parens))) () ((a)) ( (b)' . match ( parens ) ) . toEqual ( [ '(balanced ((parens)))' , '()' , '((a))' , '(b)' ] ) ;
296+ } ) ;
275297
276- it ( 'should match palindromes' , ( ) => {
277- const palindromes = regex ( { flags : 'gi' , plugins : [ recursion ] } ) `
278- (?<char> \w)
279- # Recurse, or match a lone unbalanced char in the middle
280- ((?R=15) | \w?)
281- \k<char>
282- ` ;
283- expect ( 'Racecar, ABBA, and redivided' . match ( palindromes ) ) . toEqual ( [ 'Racecar' , 'ABBA' , 'edivide' ] ) ;
284- } ) ;
298+ it ( 'should match palindromes' , ( ) => {
299+ const palindromes = regex ( { flags : 'gi' , plugins : [ recursion ] } ) `
300+ (?<char> \w)
301+ # Recurse, or match a lone unbalanced char in the middle
302+ ((?R=15) | \w?)
303+ \k<char>
304+ ` ;
305+ expect ( 'Racecar, ABBA, and redivided' . match ( palindromes ) ) . toEqual ( [ 'Racecar' , 'ABBA' , 'edivide' ] ) ;
306+ } ) ;
285307
286- it ( 'should match palindromes as complete words' , ( ) => {
287- const palindromeWords = regex ( { flags : 'gi' , plugins : [ recursion ] } ) `
288- \b
289- (?<palindrome>
290- (?<char> \w )
291- # Recurse, or match a lone unbalanced char in the center
292- ( \g<palindrome&R=15> | \w? )
293- \k<char>
294- )
295- \b
296- ` ;
297- expect ( 'Racecar, ABBA, and redivided' . match ( palindromeWords ) ) . toEqual ( [ 'Racecar' , 'ABBA' ] ) ;
298- } ) ;
308+ it ( 'should match palindromes as complete words' , ( ) => {
309+ const palindromeWords = regex ( { flags : 'gi' , plugins : [ recursion ] } ) `
310+ \b
311+ (?<palindrome>
312+ (?<char> \w )
313+ # Recurse, or match a lone unbalanced char in the center
314+ ( \g<palindrome&R=15> | \w? )
315+ \k<char>
316+ )
317+ \b
318+ ` ;
319+ expect ( 'Racecar, ABBA, and redivided' . match ( palindromeWords ) ) . toEqual ( [ 'Racecar' , 'ABBA' ] ) ;
299320 } ) ;
300321} ) ;
0 commit comments