Skip to content

Commit

Permalink
Test backref multiplexing with subroutines
Browse files Browse the repository at this point in the history
  • Loading branch information
slevithan committed Oct 29, 2024
1 parent 135f806 commit 93ba8be
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 21 deletions.
26 changes: 14 additions & 12 deletions spec/helpers/matchers.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,20 @@ import {toRegExp} from '../../dist/index.mjs';
import {EsVersion} from '../../src/utils.js';

function getArgs(actual, expected) {
const pattern = typeof expected === 'string' ? expected : expected.pattern;
const flags = expected.flags ?? '';
const strings = Array.isArray(actual) ? actual : [actual];
let targets = ['ES2018', 'ES2024', 'ESNext'];
if (expected.targetMax) {
targets = targets.filter(target => EsVersion[target] <= EsVersion[expected.targetMax]);
}
const opts = {
pattern: typeof expected === 'string' ? expected : expected.pattern,
flags: expected.flags ?? '',
maxTarget: expected.maxTarget ?? null,
};
const targets = ['ES2018', 'ES2024', 'ESNext'];
const targeted = opts.maxTarget ?
targets.filter(target => EsVersion[target] <= EsVersion[opts.maxTarget]) :
targets;
return {
pattern,
flags,
strings,
targets,
pattern: opts.pattern,
flags: opts.flags,
strings: Array.isArray(actual) ? actual : [actual],
targets: targeted,
};
}

Expand All @@ -37,7 +39,7 @@ function matchWithAllTargets({pattern, flags, strings, targets}, {exact, negate}
if (failed) {
return {
pass: false,
message: `Expected "${pattern}" ${negate ? 'not ' : ''}to ${exact ? 'exactly ' : ''}match "${str}" with ${flags ? `flags "${flags}" and ` : ''}target ${target}`,
message: `Expected "${pattern}" ${flags ? `(flags ${flags}) ` : ''}${negate ? 'not ' : ''}to ${exact ? 'exactly match' : 'match within'} "${str}" (${target})`,
};
}
}
Expand Down
67 changes: 58 additions & 9 deletions spec/match-backreference.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ beforeEach(() => {
});

describe('Backreference', () => {
// TODO: Backref to not-yet-closed parent group

describe('numbered backref', () => {
it('should rematch the captured text', () => {
expect('aa').toExactlyMatch(r`(a)\1`);
Expand Down Expand Up @@ -41,6 +43,15 @@ describe('Backreference', () => {
it('should throw for mixed named capture and numbered backrefs', () => {
expect(() => compile(r`(?<a>)\1`)).toThrow();
});

it('should ref the most recent of a capture/subroutine set without multiplexing', () => {
expect('abb').toExactlyMatch(r`(\w)\g<1>\1`);
expect('aba').not.toFindMatch(r`(\w)\g<1>\1`);
expect('abb').toExactlyMatch(r`\g<1>(\w)\1`);
expect('aba').not.toFindMatch(r`\g<1>(\w)\1`);
expect('1233').toExactlyMatch(r`(([123]))\g<1>\g<1>\2`);
expect(['1231', '1232']).not.toFindMatch(r`(([123]))\g<1>\g<1>\2`);
});
});

describe('enclosed numbered backref', () => {
Expand Down Expand Up @@ -89,9 +100,18 @@ describe('Backreference', () => {
expect(() => compile(r`(?<a>)\k<1>`)).toThrow();
expect(() => compile(r`(?<a>)\k'1'`)).toThrow();
});

it('should ref the most recent of a capture/subroutine set without multiplexing', () => {
expect('abb').toExactlyMatch(r`(\w)\g<1>\k<1>`);
expect('aba').not.toFindMatch(r`(\w)\g<1>\k<1>`);
expect('abb').toExactlyMatch(r`\g<1>(\w)\k<1>`);
expect('aba').not.toFindMatch(r`\g<1>(\w)\k<1>`);
expect('1233').toExactlyMatch(r`(([123]))\g<1>\g<1>\k<2>`);
expect(['1231', '1232']).not.toFindMatch(r`(([123]))\g<1>\g<1>\k<2>`);
});
});

describe('relative backref number', () => {
describe('enclosed relative numbered backref', () => {
it('should rematch the captured text', () => {
expect('aa').toExactlyMatch(r`(a)\k<-1>`);
expect('aa').toExactlyMatch(r`(a)\k'-1'`);
Expand Down Expand Up @@ -142,6 +162,15 @@ describe('Backreference', () => {
expect(() => compile(r`()\k<+1>()`)).toThrow();
expect(() => compile(r`()\k'+1'()`)).toThrow();
});

it('should ref the most recent of a capture/subroutine set without multiplexing', () => {
expect('abb').toExactlyMatch(r`(\w)\g<1>\k<-1>`);
expect('aba').not.toFindMatch(r`(\w)\g<1>\k<-1>`);
expect('abb').toExactlyMatch(r`\g<1>(\w)\k<-1>`);
expect('aba').not.toFindMatch(r`\g<1>(\w)\k<-1>`);
expect('1233').toExactlyMatch(r`(([123]))\g<1>\g<-2>\k<-1>`);
expect(['1231', '1232']).not.toFindMatch(r`(([123]))\g<1>\g<-2>\k<-1>`);
});
});

describe('named backref', () => {
Expand Down Expand Up @@ -173,19 +202,39 @@ describe('Backreference', () => {
expect('aab').toExactlyMatch(r`(?<n>a)\k<n>(?<n>b)`);
expect('aa').toExactlyMatch({
pattern: r`(?<n>a)\k<n>|(?<n>b)`,
targetMax: duplicateCaptureNamesSupported ? null : 'ES2024',
maxTarget: duplicateCaptureNamesSupported ? null : 'ES2024',
});
});

it('should multiplex for duplicate names to the left', () => {
expect([
'aba', 'abb',
]).toExactlyMatch(r`(?<n>a)(?<n>b)\k<n>`);
expect([
'abca', 'abcb', 'abcc',
]).toExactlyMatch(r`(?<n>a)(?<n>b)(?<n>c)\k<n>`);
expect(['aba', 'abb']).toExactlyMatch(r`(?<n>a)(?<n>b)\k<n>`);
expect(['abca', 'abcb', 'abcc']).toExactlyMatch(r`(?<n>a)(?<n>b)(?<n>c)\k<n>`);
expect(['aba', 'abb']).toExactlyMatch(r`(?<n>\w)(?<n>\w)\k<n>`);
expect(['aab', 'abc']).not.toFindMatch(r`(?<n>\w)(?<n>\w)\k<n>`);
});

// TODO: Subroutine backrefs
// TODO: Multiplexing changes across multiple backrefs as more duplicate groups are added

it('should ref the most recent of a capture/subroutine set without multiplexing', () => {
expect('abb').toExactlyMatch(r`(?<a>\w)\g<a>\k<a>`);
expect('aba').not.toFindMatch(r`(?<a>\w)\g<a>\k<a>`);
expect('abb').toExactlyMatch(r`\g<a>(?<a>\w)\k<a>`);
expect('aba').not.toFindMatch(r`\g<a>(?<a>\w)\k<a>`);
});

it('should multiplex for duplicate names to the left but use only the most recent of an indirect capture/subroutine set', () => {
expect([
'1010', '1011', '1020', '1022', '2010', '2011', '2020', '2022',
]).toExactlyMatch(r`(?<a>(?<b>[12]))(?<b>0)\g<a>\k<b>`);
expect(['1021', '2012']).not.toFindMatch(r`(?<a>(?<b>[12]))(?<b>0)\g<a>\k<b>`);

// Not listing all possible matches below...
expect(['01230', '01233']).toExactlyMatch(r`(?<b>0)(?<a>(?<b>[123]))\g<a>\g<a>\k<b>`);
expect(['01231', '01232']).not.toFindMatch(r`(?<b>0)(?<a>(?<b>[123]))\g<a>\g<a>\k<b>`);
expect(['10230', '10233']).toExactlyMatch(r`(?<a>(?<b>[123]))(?<b>0)\g<a>\g<a>\k<b>`);
expect(['10231', '10232']).not.toFindMatch(r`(?<a>(?<b>[123]))(?<b>0)\g<a>\g<a>\k<b>`);
expect(['12300', '12303']).toExactlyMatch(r`(?<a>(?<b>[123]))\g<a>\g<a>(?<b>0)\k<b>`);
expect(['12301', '12302']).not.toFindMatch(r`(?<a>(?<b>[123]))\g<a>\g<a>(?<b>0)\k<b>`);
});
});
});
1 change: 1 addition & 0 deletions spec/match-char.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ describe('Character', () => {
describe('literal', () => {
it('should match literal chars', () => {
expect('a').toExactlyMatch('a');
expect('😊').toExactlyMatch('😊'); // U+1F60A
expect('Multiple literal chars!').toExactlyMatch('Multiple literal chars!');
});
});
Expand Down

0 comments on commit 93ba8be

Please sign in to comment.