blob: b43dc2ea199fd8fc348210349b5253d26aa386e6 [file] [edit]
// With verbose set to false, this test is successful if there is no output. Set verbose to true to see expected matches.
let verbose = false;
function arrayToString(arr)
{
let str = '';
arr.forEach(function(v, index) {
if (typeof v == "string")
str += "\"" + v + "\"";
else
str += v;
if (index != (arr.length - 1))
str += ',';
});
return str;
}
function objectToString(obj)
{
let str = "";
firstEntry = true;
for (const [key, value] of Object.entries(obj)) {
if (!firstEntry)
str += ", ";
str += key + ": " + dumpValue(value);
firstEntry = false;
}
return "{ " + str + " }";
}
function dumpValue(v)
{
if (v === null)
return "<null>";
if (v === undefined)
return "<undefined>";
if (typeof v == "string")
return "\"" + v + "\"";
let str = "";
if (v.length)
str += arrayToString(v);
if (v.groups) {
groupStr = objectToString(v.groups);
if (str.length) {
if ( groupStr.length)
str += ", " + groupStr;
} else
str = groupStr;
}
return "[ " + str + " ]";
}
function compareArray(expected, actual)
{
if (expected === null && actual === null)
return true;
if (expected === null) {
print("### expected is null, actual is not null");
return false;
}
if (actual === null) {
print("### expected is not null, actual is null");
return false;
}
if (expected.length !== actual.length) {
print("### expected.length: " + expected.length + ", actual.length: " + actual.length);
return false;
}
for (var i = 0; i < expected.length; i++) {
if (expected[i] !== actual[i]) {
print("### expected[" + i + "]: \"" + expected[i] + "\" !== actual[" + i + "]: \"" + actual[i] + "\"");
return false;
}
}
return true;
}
function compareGroups(expected, actual)
{
if (expected === null && actual === null)
return true;
if (expected === null) {
print("### expected group is null, actual group is not null");
return false;
}
if (actual === null) {
print("### expected group is not null, actual group is null");
return false;
}
for (const key in expected) {
if (expected[key] !== actual[key]) {
print("### expected." + key + ": " + dumpValue(expected[key]) + " !== actual." + key + ": " + dumpValue(actual[key]));
return false;
}
}
return true;
}
let testNumber = 0;
function testRegExp(re, str, exp, groups)
{
testNumber++;
let actual = re.exec(str);
let result = compareArray(exp, actual);;
if (exp) {
if (groups && !compareGroups(groups, actual.groups))
result = false;
}
if (result) {
if (verbose)
print(re.toString() + ".exec(" + dumpValue(str) + "), passed ", dumpValue(exp));
} else
print(re.toString() + ".exec(" + dumpValue(str) + "), FAILED test #" + testNumber + ", Expected ", dumpValue(exp), " got ", dumpValue(actual));
}
function testRegExpSyntaxError(reString, flags, expError)
{
testNumber++;
let threw = false;
try {
let re = new RegExp(reString, flags);
} catch (e) {
threw = true;
if (e != expError)
print("FAILED test #" + testNumber + ", Expected /" + reString + "/" + flags + " to throw \"" + expError + "\" got \"" + e + "\"");
else if (verbose)
print("/" + reString + "/" + flags + " passed, it threw \"" + expError + "\" as expected");
}
if (!threw)
print("FAILED test #" + testNumber + ", Expected /" + reString + "/" + flags + " to throw \"" + expError + "\", but it didn't");
}
// Test 1
testRegExp(/(?<A>123)|(?<A>456)/, "123", ["123", "123", undefined], { A: "123" });
testRegExp(/(?<A>123)|(?<A>456)/, "456", ["456", undefined, "456"], { A: "456" });
testRegExp(/(?:(?<mon>[1-9]|1[0-2])\/(?<day>[1-9]|[12][0-9]|3[01])\/(?<yr>19[6-9][0-9]|20[0-2][0-9]))|(?:(?<day>[1-9]|[12][0-9]|3[01])\-(?<mon>[1-9]|1[0-2])\-(?<yr>19[6-9][0-9]|20[0-2][0-9]))/, "3/24/1992", ["3/24/1992", "3", "24", "1992", undefined, undefined, undefined], { mon: "3", day: "24", yr: "1992" });
testRegExp(/(?:(?<mon>[1-9]|1[0-2])\/(?<day>[1-9]|[12][0-9]|3[01])\/(?<yr>19[6-9][0-9]|20[0-2][0-9]))|(?:(?<day>[1-9]|[12][0-9]|3[01])\-(?<mon>[1-9]|1[0-2])\-(?<yr>19[6-9][0-9]|20[0-2][0-9]))/, "24-3-1992", ["24-3-1992", undefined, undefined, undefined, "24", "3", "1992"], { mon: "3", day: "24", yr: "1992" });
testRegExp(/(?<x>a)|(?<x>b)/, "bab", ["b", undefined, "b"]);
// Test 6
testRegExp(/(?:(?<x>a)|(?<x>b))\k<x>/, "bb", ["bb", undefined, "b"], { x: "b"});
testRegExp(/(?:(?:(?<x>a)|(?<x>b))\k<x>){2}/, "aabb", ["aabb", undefined, "b"], { x: "b" });
testRegExp(/(?<A>abc)x|(?<A>abc)y|abcz/, "abcx", ["abcx", "abc", undefined], { A: "abc" });
testRegExp(/(?<A>abc)x|(?<A>abc)y|abcz/, "abcy", ["abcy", undefined, "abc"], { A: "abc" });
testRegExp(/(?<A>abc)x|(?<A>abc)y|abcz/, "abcz", ["abcz", undefined, undefined], { A: undefined });
// Test 11
testRegExp(/(?<A>abc)x|(?<B>abc)y|abcz/, "abcz", ["abcz", undefined, undefined], { A: undefined, B: undefined });
testRegExp(/(?<x>a)|(?<x>b)/, "ba", ["b", undefined, "b"], { x: "b"});
testRegExp(/(?<=(?:\k<x>{0}(?<x>a)))b/, "ab", ["b", "a"], { x: "a" });
testRegExp(/(?<=(?:\k<x>(?<x>c))|(?:\k<x>(?<x>a)))b/, "aab", ["b", undefined, "a"], { x: "a" });
testRegExp(/(?<=(?:\k<x>(?<x>c))|(?:\k<x>))b/, "aab", ["b", undefined], { x: undefined });
testRegExp(/(?<=(?:\k<x>(?<x>c))|(?:\k<y>(?<y>a)))b/, "aab", ["b", undefined, "a"], { x: undefined, y: "a" });
testRegExp(/(?<!(x)\1{0})b/, "xb", null);
// Test 18
testRegExp(/^(?:(?<a>x)|(?<a>y)|z)\k<a>$/, "z", ["z", undefined, undefined]);
testRegExp(/^(?:(?<a>x)|(?<a>y)|z){2}\k<a>$/, "xz", ["xz", undefined, undefined]);
testRegExp(/(?<a>x)|(?:zy\k<a>)/, "zy", ["zy", undefined]);
testRegExpSyntaxError("(?<A>123)(?<A>456)", "", "SyntaxError: Invalid regular expression: duplicate group specifier name");
testRegExpSyntaxError("(?<A>123)(?:(?<A>456)|(?<A>789))", "", "SyntaxError: Invalid regular expression: duplicate group specifier name");
// Test 23
testRegExpSyntaxError("(?<=\\k<a>a)x", "u", "SyntaxError: Invalid regular expression: invalid \\k<> named backreference");
testRegExp(/(?<b>.).\k<b>/u, "bab", ["bab", "b"]);
testRegExp(/(?<b>.).\k<b>/u, "baa", null, null);
testRegExp(/(?<a>\k<a>\w)../u, "bab", ["bab", "b"], { a: "b" });
testRegExp(/(?:(?<x>a)|(?<y>a)(?<x>b))(?:(?<z>c)|(?<z>d))/, "abc", ["abc", undefined, "a", "b", "c", undefined], { x: "b", y: "a", z: "c" });
// Test 28
testRegExp(/(?<=(?:\1|b)(aa))./, "aaaax", ["x", "aa"]);
testRegExp(/(?<=(?:\2|b)(?<=\1(a))(aa))./, "aaaax", ["x", "a", "aa"]);
testRegExp(/(?<=((?:\3|b))(?<=\2(a))(aa))./, "aaaax", ["x", "aa", "a", "aa"]);
testRegExpSyntaxError("((?:(?<f>\w))(?<f>.)(a*c)?)*", "", "SyntaxError: Invalid regular expression: duplicate group specifier name");
// &&&& testRegExp(/((?:(?<f>\w))(?<f>.)(a*c)?)*/, "aabbbccc", ["aabbbcc","bcc","b","c","c"], { f: "c" });
testRegExp(/(?<A>)|(?<A>)*\k<A>/, "", ["", "", undefined], { A: "" });
// Test 33
testRegExp(/(?:(?<A>a)|(?<A>b)*)\k<A>/, "bb", ["bb",undefined,"b"], { A: "b" });
testRegExp(/((?<A>A+)(?<B>B+)(?<C>C+)(?<D>D+))+|((?<B>B+)(?<C>C+)(?<D>D+))+|((?<A>A+)(?<C>C+)(?<D>D+))+|((?<C>C+)(?<D>D+))+|((?<B>B+)(?<D>D+))+|((?<A>A+)(?<D>D+))+|((?<D>D+))/, "AAD", ["AAD", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, "AAD", "AA", "D", undefined, undefined], { A: "AA", B: undefined, C: undefined, D: "D" });
testRegExpSyntaxError("(?<x>c)(?:(?<x>a)|(?<x>b))", "", "SyntaxError: Invalid regular expression: duplicate group specifier name");
testRegExpSyntaxError("(?:(?<x>a)|(?<x>b))(?<x>c)", "", "SyntaxError: Invalid regular expression: duplicate group specifier name");
testRegExpSyntaxError("(?:(?<x>c)x)(?:(?<x>a)|(?<x>b))", "", "SyntaxError: Invalid regular expression: duplicate group specifier name");
// Test 38
testRegExpSyntaxError("(?:(?<x>a)|(?<x>b))(?:(?<x>c)x)", "", "SyntaxError: Invalid regular expression: duplicate group specifier name");
testRegExpSyntaxError("(?:(?<x>c)x|(?<y>d))(?:(?<y>a)|(?<x>b))", "", "SyntaxError: Invalid regular expression: duplicate group specifier name");
testRegExpSyntaxError("(?:(?<y>a)|(?<x>b))(?:(?<x>c)x|(?<y>d))", "", "SyntaxError: Invalid regular expression: duplicate group specifier name");
// Test named capture groups backtracking doesn't confuse numbered backreferences
testRegExp(/(?<l>x)|((?<l>(|)\1?.){2})x/, "caffeeeee", null);