RegExp named group tests (#998)

Tests against the Stage 3 named capture groups proposal
https://tc39.github.io/proposal-regexp-named-groups
This commit is contained in:
Daniel Ehrenberg 2017-04-27 18:50:59 +02:00 committed by Leo Balter
parent c528486da3
commit 6cf15f523a
18 changed files with 684 additions and 0 deletions

View File

@ -0,0 +1,56 @@
// Copyright 2017 the V8 project authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: >
Function argument to String.prototype.replace gets groups as the last argument
esid: sec-regexp.prototype-@@replace
features: [regexp-named-groups]
info: >
RegExp.prototype [ @@replace ] ( string, replaceValue )
14. Repeat, for each result in results,
j. Let namedCaptures be ? Get(result, "groups").
k. If functionalReplace is true, then
iv. If namedCaptures is not undefined,
1. Append namedCaptures as the last element of replacerArgs.
---*/
let source = "(?<fst>.)(?<snd>.)";
let alternateSource = "(?<fst>.)|(?<snd>.)";
for (let flags of ["g", "gu"]) {
let i = 0;
let re = new RegExp(source, flags);
let result = "abcd".replace(re,
(match, fst, snd, offset, str, groups) => {
if (i == 0) {
assert.sameValue("ab", match);
assert.sameValue("a", groups.fst);
assert.sameValue("b", groups.snd);
assert.sameValue("a", fst);
assert.sameValue("b", snd);
assert.sameValue(0, offset);
assert.sameValue("abcd", str);
} else if (i == 1) {
assert.sameValue("cd", match);
assert.sameValue("c", groups.fst);
assert.sameValue("d", groups.snd);
assert.sameValue("c", fst);
assert.sameValue("d", snd);
assert.sameValue(2, offset);
assert.sameValue("abcd", str);
} else {
assertUnreachable();
}
i++;
return `${groups.snd}${groups.fst}`;
});
assert.sameValue("badc", result);
assert.sameValue(i, 2);
let re2 = new RegExp(alternateSource, flags);
assert.sameValue("undefinedundefinedundefinedundefined",
"abcd".replace(re2,
(match, fst, snd, offset, str, groups) => groups.snd));
}

View File

@ -0,0 +1,43 @@
// Copyright 2017 the V8 project authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: >
Function argument to String.prototype.replace gets groups as the last argument
esid: sec-regexp.prototype-@@replace
features: [regexp-named-groups]
info: >
RegExp.prototype [ @@replace ] ( string, replaceValue )
14. Repeat, for each result in results,
j. Let namedCaptures be ? Get(result, "groups").
k. If functionalReplace is true, then
iv. If namedCaptures is not undefined,
1. Append namedCaptures as the last element of replacerArgs.
---*/
let source = "(?<fst>.)(?<snd>.)";
let alternateSource = "(?<fst>.)|(?<snd>.)";
for (let flags of ["", "u"]) {
let i = 0;
let re = new RegExp(source, flags);
let result = "abcd".replace(re,
(match, fst, snd, offset, str, groups) => {
assert.sameValue(i++, 0);
assert.sameValue("ab", match);
assert.sameValue("a", groups.fst);
assert.sameValue("b", groups.snd);
assert.sameValue("a", fst);
assert.sameValue("b", snd);
assert.sameValue(0, offset);
assert.sameValue("abcd", str);
return `${groups.snd}${groups.fst}`;
});
assert.sameValue("bacd", result);
assert.sameValue(i, 1);
let re2 = new RegExp(alternateSource, flags);
assert.sameValue("undefinedbcd",
"abcd".replace(re2,
(match, fst, snd, offset, str, groups) => groups.snd));
}

View File

@ -0,0 +1,39 @@
// Copyright 2017 the V8 project authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: Properties of the groups object are created with CreateDataProperty
includes: [compareArray.js, propertyHelper.js]
esid: sec-regexpbuiltinexec
features: [regexp-named-groups]
info: >
Runtime Semantics: RegExpBuiltinExec ( R, S )
25. For each integer i such that i > 0 and i n
f. If the ith capture of R was defined with a GroupName,
i. Let s be the StringValue of the corresponding RegExpIdentifierName.
ii. Perform ! CreateDataProperty(groups, s, capturedValue).
---*/
// Properties created on result.groups in textual order.
assert(compareArray(["fst", "snd"],
Object.getOwnPropertyNames(
/(?<fst>.)|(?<snd>.)/u.exec("abcd").groups)));
// Properties are created with Define, not Set
let counter = 0;
Object.defineProperty(Object.prototype, 'x', {set() { counter++; }});
let match = /(?<x>.)/.exec('a');
let groups = match.groups;
assert.sameValue(counter, 0);
// Properties are writable, enumerable and configurable
// (from CreateDataProperty)
verifyWritable(groups, "x");
verifyEnumerable(groups, "x");
verifyConfigurable(groups, "x");
// The '__proto__' property on the groups object is not special,
// and does not affect the [[Prototype]] of the resulting groups object.
groups = /(?<__proto__>a)/u.exec("a").groups;
assert.sameValue("a", groups.__proto__);
assert.sameValue(null, Object.getPrototypeOf(groups));

View File

@ -0,0 +1,46 @@
// Copyright 2018 the V8 project authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: Named groups can be used in conjunction with lookbehind
esid: pending
features: [regexp-named-groups, regexp-lookbehind]
includes: [compareArray.js]
---*/
// Unicode mode
assert(compareArray(["f", "c"], "abcdef".match(/(?<=(?<a>\w){3})f/u)));
assert.sameValue("c", "abcdef".match(/(?<=(?<a>\w){3})f/u).groups.a);
assert.sameValue("b", "abcdef".match(/(?<=(?<a>\w){4})f/u).groups.a);
assert.sameValue("a", "abcdef".match(/(?<=(?<a>\w)+)f/u).groups.a);
assert.sameValue(null, "abcdef".match(/(?<=(?<a>\w){6})f/u));
assert(compareArray(["f", ""], "abcdef".match(/((?<=\w{3}))f/u)));
assert(compareArray(["f", ""], "abcdef".match(/(?<a>(?<=\w{3}))f/u)));
assert(compareArray(["f", undefined], "abcdef".match(/(?<!(?<a>\d){3})f/u)));
assert.sameValue(null, "abcdef".match(/(?<!(?<a>\D){3})f/u));
assert(compareArray(["f", undefined], "abcdef".match(/(?<!(?<a>\D){3})f|f/u)));
assert(compareArray(["f", undefined], "abcdef".match(/(?<a>(?<!\D{3}))f|f/u)));
// Non-Unicode mode
assert(compareArray(["f", "c"], "abcdef".match(/(?<=(?<a>\w){3})f/)));
assert.sameValue("c", "abcdef".match(/(?<=(?<a>\w){3})f/).groups.a);
assert.sameValue("b", "abcdef".match(/(?<=(?<a>\w){4})f/).groups.a);
assert.sameValue("a", "abcdef".match(/(?<=(?<a>\w)+)f/).groups.a);
assert.sameValue(null, "abcdef".match(/(?<=(?<a>\w){6})f/));
assert(compareArray(["f", ""], "abcdef".match(/((?<=\w{3}))f/)));
assert(compareArray(["f", ""], "abcdef".match(/(?<a>(?<=\w{3}))f/)));
assert(compareArray(["f", undefined], "abcdef".match(/(?<!(?<a>\d){3})f/)));
assert.sameValue(null, "abcdef".match(/(?<!(?<a>\D){3})f/));
assert(compareArray(["f", undefined], "abcdef".match(/(?<!(?<a>\D){3})f|f/)));
assert(compareArray(["f", undefined], "abcdef".match(/(?<a>(?<!\D{3}))f|f/)));
// Even within a lookbehind, properties are created in left to right order
assert(compareArray(["fst", "snd"],
Object.getOwnPropertyNames(
/(?<=(?<fst>.)|(?<snd>.))/u.exec("abcd").groups)));

View File

@ -0,0 +1,44 @@
// Copyright 2017 the V8 project authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: >
Named groups in Unicode RegExps have some syntax errors and some
compatibility escape fallback behavior.
esid: pending
features: [regexp-named-groups, regexp-lookbehind]
includes: [compareArray.js]
---*/
assert.throws(SyntaxError, () => eval("/(?<>a)/"));
assert.throws(SyntaxError, () => eval("/(?<aa)/"));
assert.throws(SyntaxError, () => eval("/(?<42a>a)/"));
assert.throws(SyntaxError, () => eval("/(?<:a>a)/"));
assert.throws(SyntaxError, () => eval("/(?<a:>a)/"));
assert.throws(SyntaxError, () => eval("/(?<a>a)(?<a>a)/"));
assert.throws(SyntaxError, () => eval("/(?<a>a)(?<b>b)(?<a>a)/"));
assert(/\k<a>/.test("k<a>"));
assert(/\k<4>/.test("k<4>"));
assert(/\k<a/.test("k<a"));
assert(/\k/.test("k"));
assert.throws(SyntaxError, () => eval("/(?<a>.)\\k/"));
assert.throws(SyntaxError, () => eval("/(?<a>.)\\k<a/"));
assert.throws(SyntaxError, () => eval("/(?<a>.)\\k<b>/"));
assert.throws(SyntaxError, () => eval("/(?<a>a)\\k<ab>/"));
assert.throws(SyntaxError, () => eval("/(?<ab>a)\\k<a>/"));
assert.throws(SyntaxError, () => eval("/\\k<a>(?<ab>a)/"));
assert.throws(SyntaxError, () => eval("/\\k<a(?<a>a)/"));
assert(/(?<a>\a)/.test("a"));
assert(compareArray(["k<a>"], "xxxk<a>xxx".match(/\k<a>/)));
assert(compareArray(["k<a"], "xxxk<a>xxx".match(/\k<a/)));
// A couple of corner cases around '\k' as named back-references vs. identity
// escapes.
assert(/\k<a>(?<=>)a/.test("k<a>a"));
assert(/\k<a>(?<!a)a/.test("k<a>a"));
assert(/\k<a>(<a>x)/.test("k<a><a>x"));
assert(/\k<a>(?<a>x)/.test("x"));
assert.throws(SyntaxError, () => eval("/\\k<a>(?<b>x)/"));
assert.throws(SyntaxError, () => eval("/\\k<a(?<a>.)/"));
assert.throws(SyntaxError, () => eval("/\\k(?<a>.)/"));

View File

@ -0,0 +1,43 @@
// Copyright 2017 the V8 project authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: Basic matching cases with non-Unicode groups
esid: pending
features: [regexp-named-groups]
includes: [compareArray.js]
---*/
assert(compareArray(["a", "a"], "bab".match(/(?<a>a)/)));
assert(compareArray(["a", "a"], "bab".match(/(?<a42>a)/)));
assert(compareArray(["a", "a"], "bab".match(/(?<_>a)/)));
assert(compareArray(["a", "a"], "bab".match(/(?<$>a)/)));
assert(compareArray(["bab", "a"], "bab".match(/.(?<$>a)./)));
assert(compareArray(["bab", "a", "b"], "bab".match(/.(?<a>a)(.)/)));
assert(compareArray(["bab", "a", "b"], "bab".match(/.(?<a>a)(?<b>.)/)));
assert(compareArray(["bab", "ab"], "bab".match(/.(?<a>\w\w)/)));
assert(compareArray(["bab", "bab"], "bab".match(/(?<a>\w\w\w)/)));
assert(compareArray(["bab", "ba", "b"], "bab".match(/(?<a>\w\w)(?<b>\w)/)));
let {a, b, c} = /(?<a>.)(?<b>.)(?<c>.)\k<c>\k<b>\k<a>/.exec("abccba").groups;
assert.sameValue(a, "a");
assert.sameValue(b, "b");
assert.sameValue(c, "c");
assert(compareArray("bab".match(/(a)/), "bab".match(/(?<a>a)/)));
assert(compareArray("bab".match(/(a)/), "bab".match(/(?<a42>a)/)));
assert(compareArray("bab".match(/(a)/), "bab".match(/(?<_>a)/)));
assert(compareArray("bab".match(/(a)/), "bab".match(/(?<$>a)/)));
assert(compareArray("bab".match(/.(a)./), "bab".match(/.(?<$>a)./)));
assert(compareArray("bab".match(/.(a)(.)/), "bab".match(/.(?<a>a)(.)/)));
assert(compareArray("bab".match(/.(a)(.)/), "bab".match(/.(?<a>a)(?<b>.)/)));
assert(compareArray("bab".match(/.(\w\w)/), "bab".match(/.(?<a>\w\w)/)));
assert(compareArray("bab".match(/(\w\w\w)/), "bab".match(/(?<a>\w\w\w)/)));
assert(compareArray("bab".match(/(\w\w)(\w)/), "bab".match(/(?<a>\w\w)(?<b>\w)/)));
assert(compareArray(["bab", "b"], "bab".match(/(?<b>b).\1/)));
assert(compareArray(["baba", "b", "a"], "baba".match(/(.)(?<a>a)\1\2/)));
assert(compareArray(["baba", "b", "a", "b", "a"],
"baba".match(/(.)(?<a>a)(?<b>\1)(\2)/)));
assert(compareArray(["<a", "<"], "<a".match(/(?<lt><)a/)));
assert(compareArray([">a", ">"], ">a".match(/(?<gt>>)a/)));

View File

@ -0,0 +1,45 @@
// Copyright 2017 the V8 project authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: Exotic named group names in non-Unicode RegExps
esid: pending
features: [regexp-named-groups]
includes: [compareArray.js]
---*/
assert.sameValue("a", /(?<π>a)/.exec("bab").groups.π);
assert.throws(SyntaxError, () => eval('/(?<\\u{03C0}>a)/'), "\\u{} escapes allowed only in Unicode mode");
assert.sameValue("a", /(?<π>a)/.exec("bab").groups.\u03C0);
assert.sameValue("a", /(?<$>a)/.exec("bab").groups.$);
assert.sameValue("a", /(?<_>a)/.exec("bab").groups._);
assert.throws(SyntaxError, () => eval('/(?<$𐒤>a)/'), "Individual surrogates not in ID_Continue);
assert.sameValue("a", /(?<_\u200C>a)/.exec("bab").groups._\u200C);
assert.sameValue("a", /(?<_\u200D>a)/.exec("bab").groups._\u200D);
assert.sameValue("a", /(?<ಠ_ಠ>a)/.exec("bab").groups._ಠ);
assert.throws(SyntaxError, () => eval('/(?<❤>a)/'));
assert.throws(SyntaxError, () => eval('/(?<𐒤>a)/'), "Individual surrogate not in ID_Start.");
// Unicode escapes in capture names.
assert.throws(SyntaxError, () => eval("/(?<a\\uD801\uDCA4>.)/"));
assert.throws(SyntaxError, () => eval("/(?<a\\uD801>.)/"));
assert.throws(SyntaxError, () => eval("/(?<a\\uDCA4>.)/"));
assert(/(?<\u0041>.)/.test("a"));
assert.throws(SyntaxError, () => eval("/(?<a\\u{104A4}>.)/"));
assert.throws(SyntaxError, () => eval("/(?<a\\u{10FFFF}>.)/"));
assert.throws(SyntaxError, () => eval("/(?<a\uD801>.)/"), "Lea");
assert.throws(SyntaxError, () => eval("/(?<a\uDCA4>.)/"), "Trai");
assert(RegExp("(?<\u{0041}>.)").test("a"), "Non-surrogate");
assert(RegExp("(?<a\u{104A4}>.)").test("a"), "Surrogate, ID_Continue");
// Bracketed escapes are not allowed;
// 4-char escapes must be the proper ID_Start/ID_Continue
assert.throws(SyntaxError, () => eval("/(?<a\\uD801>.)/"), "Lead");
assert.throws(SyntaxError, () => eval("/(?<a\\uDCA4>.)/"), "Trail");
assert.throws(SyntaxError, () => eval("/(?<\\u{0041}>.)/"), "Non-surrogate");
assert.throws(SyntaxError, () => eval("/(?<a\\u{104A4}>.)/"), "Surrogate, ID_Continue");
assert(RegExp("(?<\\u0041>.)").test("a"), "Non-surrogate");
// Backslash is not allowed as ID_Start and ID_Continue
assert.throws(SyntaxError, () => eval("/(?<\\>.)/"), "'\' misclassified as ID_Start");
assert.throws(SyntaxError, () => eval("/(?<a\\>.)/"), "'\' misclassified as ID_Continue");

View File

@ -0,0 +1,34 @@
// Copyright 2017 the V8 project authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: Named backreferences in non-Unicode RegExps
esid: pending
features: [regexp-named-groups]
includes: [compareArray.js]
---*/
// Named references.
assert(compareArray(["bab", "b"], "bab".match(/(?<b>.).\k<b>/)));
assert.sameValue(null, "baa".match(/(?<b>.).\k<b>/));
// Reference inside group.
assert(compareArray(["bab", "b"], "bab".match(/(?<a>\k<a>\w)../)));
assert.sameValue("b", "bab".match(/(?<a>\k<a>\w)../).groups.a);
// Reference before group.
assert(compareArray(["bab", "b"], "bab".match(/\k<a>(?<a>b)\w\k<a>/)));
assert.sameValue("b", "bab".match(/\k<a>(?<a>b)\w\k<a>/).groups.a);
assert(compareArray(["bab", "b", "a"], "bab".match(/(?<b>b)\k<a>(?<a>a)\k<b>/)));
let {a, b} = "bab".match(/(?<b>b)\k<a>(?<a>a)\k<b>/).groups;
assert.sameValue(a, "a");
assert.sameValue(b, "b");
assert(compareArray(["bab", "b"], "bab".match(/\k<a>(?<a>b)\w\k<a>/)));
assert(compareArray(["bab", "b", "a"], "bab".match(/(?<b>b)\k<a>(?<a>a)\k<b>/)));
// Reference properties.
assert.sameValue("a", /(?<a>a)(?<b>b)\k<a>/.exec("aba").groups.a);
assert.sameValue("b", /(?<a>a)(?<b>b)\k<a>/.exec("aba").groups.b);
assert.sameValue(undefined, /(?<a>a)(?<b>b)\k<a>/.exec("aba").groups.c);
assert.sameValue(undefined, /(?<a>a)(?<b>b)\k<a>|(?<c>c)/.exec("aba").groups.c);

View File

@ -0,0 +1,29 @@
// Copyright 2017 the V8 project authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: Named substitutions are found by getting the property from the groups object
esid: sec-getsubstitution
features: [regexp-named-groups]
info: >
Runtime Semantics: GetSubstitution( matched, str, position, captures, namedCaptures, replacement )
Table: Replacement Text Symbol Substitutions
Unicode Characters: $<
Replacement text:
2. Otherwise,
c. Let capture be ? Get(namedCaptures, groupName).
d. If capture is undefined, replace the text through > with the empty string.
e. Otherwise, replace the text through this following > with ? ToString(capture).
---*/
let source = "(?<fst>.)(?<snd>.)|(?<thd>x)";
for (let flags of ["g", "gu"]) {
let re = new RegExp(source, flags);
assert.sameValue("badc", "abcd".replace(re, "$<snd>$<fst>"));
}
for (let flags of ["", "u"]) {
let re = new RegExp(source, flags);
assert.sameValue("bacd", "abcd".replace(re, "$<snd>$<fst>"));
}

View File

@ -0,0 +1,25 @@
// Copyright 2017 the V8 project authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: SyntaxError is thrown for malformed replacements
esid: sec-getsubstitution
features: [regexp-named-groups]
info: >
Runtime Semantics: GetSubstitution( matched, str, position, captures, namedCaptures, replacement )
Table: Replacement Text Symbol Substitutions
Unicode Characters: $<
Replacement text:
2. Otherwise,
b. If ? HasProperty(namedCaptures, groupName) is false, throw a SyntaxError exception.
---*/
let source = "(?<fst>.)(?<snd>.)|(?<thd>x)";
for (let flags of ["", "u", "g", "gu"]) {
let re = new RegExp(source, flags);
assert.throws(SyntaxError, () => "abcd".replace(re, "$<42$1>"));
assert.throws(SyntaxError, () => "abcd".replace(re, "$<fth>"));
assert.throws(SyntaxError, () => "abcd".replace(re, "$<$1>"));
}

View File

@ -0,0 +1,31 @@
// Copyright 2017 the V8 project authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: If there are no named captures, don't replace $<>
esid: sec-getsubstitution
features: [regexp-named-groups]
info: >
Runtime Semantics: GetSubstitution( matched, str, position, captures, namedCaptures, replacement )
Table: Replacement Text Symbol Substitutions
Unicode Characters: $<
Replacement text:
1. If namedCaptures is undefined, the replacement text is the literal string $<.
---*/
// @@replace with a string replacement argument (no named captures).
let source = "(.)(.)|(x)";
for (let flags of ["", "u", "g", "gu"]) {
let re = new RegExp(source, flags);
assert.sameValue("$<snd>$<fst>cd", "abcd".replace(re, "$<snd>$<fst>"));
assert.sameValue("bacd", "abcd".replace(re, "$2$1"));
assert.sameValue("cd", "abcd".replace(re, "$3"));
assert.sameValue("$<sndcd", "abcd".replace(re, "$<snd"));
assert.sameValue("$<42a>cd", "abcd".replace(re, "$<42$1>"));
assert.sameValue("$<fth>cd", "abcd".replace(re, "$<fth>"));
assert.sameValue("$<a>cd", "abcd".replace(re, "$<$1>"));
}

View File

@ -0,0 +1,29 @@
// Copyright 2017 the V8 project authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: Named groups may be accessed in their replacement string by number
esid: sec-getsubstitution
features: [regexp-named-groups]
info: >
Runtime Semantics: GetSubstitution( matched, str, position, captures, namedCaptures, replacement )
Table: Replacement Text Symbol Substitutions
Unicode Characters: $n
Replacement text:
The nth element of captures, where n is a single digit in the range 1 to 9. If
nm and the nth element of captures is undefined, use the empty String instead.
If n>m, the result is implementation-defined.
---*/
let source = "(?<fst>.)(?<snd>.)|(?<thd>x)";
for (let flags of ["g", "gu"]) {
let re = new RegExp(source, flags);
assert.sameValue("badc", "abcd".replace(re, "$2$1"));
}
for (let flags of ["", "u"]) {
let re = new RegExp(source, flags);
assert.sameValue("bacd", "abcd".replace(re, "$2$1"));
}

View File

@ -0,0 +1,24 @@
// Copyright 2017 the V8 project authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: SyntaxError is thrown for malformed replacements
esid: sec-getsubstitution
features: [regexp-named-groups]
info: >
Runtime Semantics: GetSubstitution( matched, str, position, captures, namedCaptures, replacement )
Table: Replacement Text Symbol Substitutions
Unicode Characters: $<
Replacement text:
2. Otherwise,
a. Scan until the next >, throwing a SyntaxError exception if one is not found, and let the enclosed substring be groupName.
---*/
let source = "(?<fst>.)(?<snd>.)|(?<thd>x)";
for (let flags of ["", "u", "g", "gu"]) {
let re = new RegExp(source, flags);
assert.throws(SyntaxError, () => "abcd".replace(re, "$<snd"),
"unclosed named group in replacement should throw a SyntaxError");
}

View File

@ -0,0 +1,28 @@
// Copyright 2017 the V8 project authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: If a named group was not reached, it is replaced by the empty string
esid: sec-getsubstitution
features: [regexp-named-groups]
info: >
Runtime Semantics: GetSubstitution( matched, str, position, captures, namedCaptures, replacement )
Table: Replacement Text Symbol Substitutions
Unicode Characters: $<
Replacement text:
2. Otherwise,
c. Let capture be ? Get(namedCaptures, groupName).
d. If capture is undefined, replace the text through > with the empty string.
---*/
let source = "(?<fst>.)(?<snd>.)|(?<thd>x)";
for (let flags of ["g", "gu"]) {
let re = new RegExp(source, flags);
assert.sameValue("", "abcd".replace(re, "$<thd>"));
}
for (let flags of ["", "u"]) {
let re = new RegExp(source, flags);
assert.sameValue("cd", "abcd".replace(re, "$<thd>"));
}

View File

@ -0,0 +1,27 @@
// Copyright 2017 the V8 project authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: Various syntax errors for Unicode RegExps containing named groups
esid: pending
features: [regexp-named-groups]
---*/
assert.throws(SyntaxError, () => eval("/(?<>a)/u"), "Empty name");
assert.throws(SyntaxError, () => eval("/(?<aa)/u"), "Unterminated name");
assert.throws(SyntaxError, () => eval("/(?<42a>a)/u"), "Name starting with digits");
assert.throws(SyntaxError, () => eval("/(?<:a>a)/u"), "Name starting with invalid char");
assert.throws(SyntaxError, () => eval("/(?<a:>a)/u"), "Name containing with invalid char");
assert.throws(SyntaxError, () => eval("/(?<a>a)(?<a>a)/u"), "Duplicate name");
assert.throws(SyntaxError, () => eval("/(?<a>a)(?<b>b)(?<a>a)/u"), "Duplicate name");
assert.throws(SyntaxError, () => eval("/\\k<a>/u"), "Invalid reference");
assert.throws(SyntaxError, () => eval("/\\k<a/u"), "Unterminated reference");
assert.throws(SyntaxError, () => eval("/\\k/u"), "Lone \k");
assert.throws(SyntaxError, () => eval("/(?<a>.)\\k/u"), "Lone \k");
assert.throws(SyntaxError, () => eval("/(?<a>.)\\k<a/u"), "Unterminated reference");
assert.throws(SyntaxError, () => eval("/(?<a>.)\\k<b>/u"), "Invalid reference");
assert.throws(SyntaxError, () => eval("/(?<a>a)\\k<ab>/u"), "Invalid reference");
assert.throws(SyntaxError, () => eval("/(?<ab>a)\\k<a>/u"), "Invalid reference");
assert.throws(SyntaxError, () => eval("/\\k<a>(?<ab>a)/u"), "Invalid reference");
assert.throws(SyntaxError, () => eval("/(?<a>\\a)/u"), "Identity escape in capture");

View File

@ -0,0 +1,48 @@
// Copyright 2017 the V8 project authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: Basic matching cases with Unicode groups
esid: pending
features: [regexp-named-groups]
includes: [compareArray.js]
---*/
assert(compareArray(["a", "a"], "bab".match(/(?<a>a)/u)));
assert(compareArray(["a", "a"], "bab".match(/(?<a42>a)/u)));
assert(compareArray(["a", "a"], "bab".match(/(?<_>a)/u)));
assert(compareArray(["a", "a"], "bab".match(/(?<$>a)/u)));
assert(compareArray(["bab", "a"], "bab".match(/.(?<$>a)./u)));
assert(compareArray(["bab", "a", "b"], "bab".match(/.(?<a>a)(.)/u)));
assert(compareArray(["bab", "a", "b"], "bab".match(/.(?<a>a)(?<b>.)/u)));
assert(compareArray(["bab", "ab"], "bab".match(/.(?<a>\w\w)/u)));
assert(compareArray(["bab", "bab"], "bab".match(/(?<a>\w\w\w)/u)));
assert(compareArray(["bab", "ba", "b"], "bab".match(/(?<a>\w\w)(?<b>\w)/u)));
let {a, b, c} = /(?<a>.)(?<b>.)(?<c>.)\k<c>\k<b>\k<a>/u.exec("abccba").groups;
assert.sameValue(a, "a");
assert.sameValue(b, "b");
assert.sameValue(c, "c");
assert(compareArray("bab".match(/(a)/u), "bab".match(/(?<a>a)/u)));
assert(compareArray("bab".match(/(a)/u), "bab".match(/(?<a42>a)/u)));
assert(compareArray("bab".match(/(a)/u), "bab".match(/(?<_>a)/u)));
assert(compareArray("bab".match(/(a)/u), "bab".match(/(?<$>a)/u)));
assert(compareArray("bab".match(/.(a)./u), "bab".match(/.(?<$>a)./u)));
assert(compareArray("bab".match(/.(a)(.)/u), "bab".match(/.(?<a>a)(.)/u)));
assert(compareArray("bab".match(/.(a)(.)/u), "bab".match(/.(?<a>a)(?<b>.)/u)));
assert(compareArray("bab".match(/.(\w\w)/u), "bab".match(/.(?<a>\w\w)/u)));
assert(compareArray("bab".match(/(\w\w\w)/u), "bab".match(/(?<a>\w\w\w)/u)));
assert(compareArray("bab".match(/(\w\w)(\w)/u), "bab".match(/(?<a>\w\w)(?<b>\w)/u)));
assert(compareArray(["bab", "b"], "bab".match(/(?<b>b).\1/u)));
assert(compareArray(["baba", "b", "a"], "baba".match(/(.)(?<a>a)\1\2/u)));
assert(compareArray(["baba", "b", "a", "b", "a"],
"baba".match(/(.)(?<a>a)(?<b>\1)(\2)/u)));
assert(compareArray(["<a", "<"], "<a".match(/(?<lt><)a/u)));
assert(compareArray([">a", ">"], ">a".match(/(?<gt>>)a/u)));
// Nested groups.
assert(compareArray(["bab", "bab", "ab", "b"], "bab".match(/(?<a>.(?<b>.(?<c>.)))/u)));
assert(compareArray({a: "bab", b: "ab", c: "b"},
"bab".match(/(?<a>.(?<b>.(?<c>.)))/u).groups));

View File

@ -0,0 +1,46 @@
// Copyright 2017 the V8 project authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: Exotic named group names in Unicode RegExps
esid: pending
features: [regexp-named-groups]
---*/
assert.sameValue("a", /(?<π>a)/u.exec("bab").groups.π);
assert.sameValue("a", /(?<\u{03C0}>a)/u.exec("bab").groups.π);
assert.sameValue("a", /(?<π>a)/u.exec("bab").groups.\u03C0);
assert.sameValue("a", /(?<\u{03C0}>a)/u.exec("bab").groups.\u03C0);
assert.sameValue("a", /(?<$>a)/u.exec("bab").groups.$);
assert.sameValue("a", /(?<_>a)/u.exec("bab").groups._);
assert.sameValue("a", /(?<$𐒤>a)/u.exec("bab").groups.$𐒤);
assert.sameValue("a", /(?<_\u200C>a)/u.exec("bab").groups._\u200C);
assert.sameValue("a", /(?<_\u200D>a)/u.exec("bab").groups._\u200D);
assert.sameValue("a", /(?<ಠ_ಠ>a)/u.exec("bab").groups._ಠ);
assert.throws(SyntaxError, () => eval('/(?<❤>a)/u'));
assert.throws(SyntaxError, () => eval('/(?<𐒤>a)/u'), "ID_Continue but not ID_Start.");
// Unicode escapes in capture names.
assert(/(?<a\uD801\uDCA4>.)/u.test("a"), "\\u Lead \\u Trail");
assert.throws(SyntaxError, () => eval("/(?<a\\uD801>.)/u"), "\\u Lea");
assert.throws(SyntaxError, () => eval("/(?<a\\uDCA4>.)/u"), "\\u Trai");
assert(/(?<\u0041>.)/u.test("a"), "\\u NonSurrogate");
assert(/(?<\u{0041}>.)/u.test("a"), "\\u{ Non-surrogate }");
assert(/(?<a\u{104A4}>.)/u.test("a"), "\\u{ Surrogate, ID_Continue }");
assert.throws(SyntaxError, () => eval("/(?<a\\u{110000}>.)/u"), "\\u{ Out-of-bounds ");
assert.throws(SyntaxError, () => eval("/(?<a\uD801>.)/u"), "Lea");
assert.throws(SyntaxError, () => eval("/(?<a\uDCA4>.)/u"), "Trai");
assert(RegExp("(?<\u{0041}>.)", "u").test("a"), "Non-surrogate");
assert(RegExp("(?<a\u{104A4}>.)", "u").test("a"), "Surrogate,ID_Continue");
// Bracketed escapes are not allowed;
// 4-char escapes must be the proper ID_Start/ID_Continue
assert.throws(SyntaxError, () => eval("/(?<a\\uD801>.)/u"), "Lead");
assert.throws(SyntaxError, () => eval("/(?<a\\uDCA4>.)/u"), "Trail");
assert((/(?<\u{0041}>.)/u).test("a"), "Non-surrogate");
assert(/(?<a\u{104A4}>.)/u.test("a"), "Surrogate, ID_Continue");
assert(RegExp("(?<\\u0041>.)", "u").test("a"), "Non-surrogate");
// Backslash is not allowed as ID_Start and ID_Continue
assert.throws(SyntaxError, () => eval("/(?<\\>.)/u"), "'\' misclassified as ID_Start");
assert.throws(SyntaxError, () => eval("/(?<a\\>.)/u"), "'\' misclassified as ID_Continue");

View File

@ -0,0 +1,47 @@
// Copyright 2017 the V8 project authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: Named backreferences in Unicode RegExps
esid: sec-atomescape
info: >
The production AtomEscape :: [+N] k GroupName evaluates as follows:
1. Search the enclosing RegExp for an instance of a GroupSpecifier for an
RegExpIdentifierName which has a StringValue equal to the StringValue
of the RegExpIdentifierName contained in GroupName.
2. Assert: A unique such GroupSpecifier is found.
3. Let parenIndex be the number of left capturing parentheses in the entire
regular expression that occur to the left of the located GroupSpecifier.
This is the total number of times the Atom::(GroupSpecifierDisjunction)
production is expanded prior to that production's Term plus the total
number of Atom :: (GroupSpecifierDisjunction) productions enclosing this Term.
4. Call BackreferenceMatcher(parenIndex) and return its Matcher result.
features: [regexp-named-groups]
includes: [compareArray.js]
---*/
// Named references.
assert(compareArray(["bab", "b"], "bab".match(/(?<b>.).\k<b>/u)));
assert.sameValue(null, "baa".match(/(?<b>.).\k<b>/u));
// Reference inside group.
assert(compareArray(["bab", "b"], "bab".match(/(?<a>\k<a>\w)../u)));
assert.sameValue("b", "bab".match(/(?<a>\k<a>\w)../u).groups.a);
// Reference before group.
assert(compareArray(["bab", "b"], "bab".match(/\k<a>(?<a>b)\w\k<a>/u)));
assert.sameValue("b", "bab".match(/\k<a>(?<a>b)\w\k<a>/u).groups.a);
assert(compareArray(["bab", "b", "a"], "bab".match(/(?<b>b)\k<a>(?<a>a)\k<b>/u)));
let {a, b} = "bab".match(/(?<b>b)\k<a>(?<a>a)\k<b>/u).groups;
assert.sameValue(a, "a");
assert.sameValue(b, "b");
assert(compareArray(["bab", "b"], "bab".match(/\k<a>(?<a>b)\w\k<a>/)));
assert(compareArray(["bab", "b", "a"], "bab".match(/(?<b>b)\k<a>(?<a>a)\k<b>/)));
// Reference properties.
assert.sameValue("a", /(?<a>a)(?<b>b)\k<a>/u.exec("aba").groups.a);
assert.sameValue("b", /(?<a>a)(?<b>b)\k<a>/u.exec("aba").groups.b);
assert.sameValue(undefined, /(?<a>a)(?<b>b)\k<a>/u.exec("aba").groups.c);
assert.sameValue(undefined, /(?<a>a)(?<b>b)\k<a>|(?<c>c)/u.exec("aba").groups.c);