mirror of https://github.com/tc39/test262.git
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:
parent
c528486da3
commit
6cf15f523a
|
@ -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));
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
|
@ -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));
|
|
@ -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)));
|
|
@ -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>.)/"));
|
|
@ -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/)));
|
|
@ -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");
|
|
@ -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);
|
|
@ -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>"));
|
||||
}
|
|
@ -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>"));
|
||||
}
|
|
@ -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>"));
|
||||
}
|
||||
|
|
@ -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
|
||||
n≤m 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"));
|
||||
}
|
||||
|
|
@ -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");
|
||||
}
|
|
@ -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>"));
|
||||
}
|
|
@ -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");
|
||||
|
|
@ -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));
|
|
@ -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");
|
|
@ -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);
|
Loading…
Reference in New Issue