Skip to content

Commit

Permalink
es5-shim: fix groups with quantifiers preventing its exercise
Browse files Browse the repository at this point in the history
  • Loading branch information
elitvyakov authored and ljharb committed Nov 24, 2016
1 parent 07a88d8 commit 54e30ea
Showing 1 changed file with 78 additions and 7 deletions.
85 changes: 78 additions & 7 deletions es5-shim.js
Original file line number Diff line number Diff line change
Expand Up @@ -1924,23 +1924,94 @@
StringPrototype.replace = function replace(searchValue, replaceValue) {
var isFn = isCallable(replaceValue);
var hasCapturingGroups = isRegex(searchValue) && (/\)[*?]/).test(searchValue.source);
var extendedGroups = getExtendedGroups(searchValue);

if (!isFn || !hasCapturingGroups) {
return str_replace.call(this, searchValue, replaceValue);
} else {
var wrappedReplaceValue = function (match) {
var length = arguments.length;
var originalLastIndex = searchValue.lastIndex;
searchValue.lastIndex = 0; // eslint-disable-line no-param-reassign
var args = searchValue.exec(match) || [];
searchValue.lastIndex = originalLastIndex; // eslint-disable-line no-param-reassign
pushCall(args, arguments[length - 2], arguments[length - 1]);
return replaceValue.apply(this, args);
var groups = ArrayPrototype.slice.call(arguments, 1, -2);

for (var i = 0; i < groups.length; i++) {
var argumentIndex = i + 1;
if (groups[i] === '' && extendedGroups[i].mayBeMissing) {
arguments[argumentIndex] = undefined;
}
}

return replaceValue.apply(this, arguments);
};
return str_replace.call(this, searchValue, wrappedReplaceValue);
}
};
}

/**
* @param regexp {RegExp}
* @returns {Object[]} groups
* @return {number} groups[].number - The number of group.
* @return {boolean} groups[].mayBeMissing - The group with quantifiers preventing its exercise, the matched text
* for a capturing group is now undefined instead of an empty string.
* The group without quantifiers, may be empty string.
* @return {boolean} groups[].isResulting - The groups with ?:, ?=, ?! are non-capturing groups.
*/
function getExtendedGroups (regexp) {
var source = regexp.source;
var result = [];
var prevFirst,
prevSecond,
nextFirst,
nextSecond;
var groupNumberCounter = 0;

for (var charIndex in source) {
var char = source[+charIndex];
prevFirst = source[+charIndex - 1] || null;
prevSecond = source[+charIndex - 2] || null;
nextFirst = source[+charIndex + 1] || null;
nextSecond = source[+charIndex + 2] || null;

if (char === '(') {
if (prevFirst && prevFirst === '\\') continue;

if (nextFirst && nextFirst === '?' && nextSecond && nextSecond === ':' ||
nextFirst && nextFirst === '?' && nextSecond && nextSecond === '=' ||
nextFirst && nextFirst === '?' && nextSecond && nextSecond === '!') {
result.push({
number: ++groupNumberCounter,
mayBeMissing: true,
isResulting: false
});
continue;
}

result.push({
number: ++groupNumberCounter,
mayBeMissing: false,
isResulting: true
});
}

if (char === ')') {
var groupIndex = groupNumberCounter - 1;

if (prevFirst && prevFirst === '\\') continue;

if (result[groupIndex] && !result[groupIndex].isResulting) {
result.splice(groupIndex, 1);
groupNumberCounter = --groupNumberCounter;
continue;
}

if (nextFirst && nextFirst === '?' || nextFirst === '*') {
result[groupIndex].mayBeMissing = true;
}
}
}

return result;
}

// ECMA-262, 3rd B.2.3
// Not an ECMAScript standard, although ECMAScript 3rd Edition has a
// non-normative section suggesting uniform semantics and it should be
Expand Down

0 comments on commit 54e30ea

Please sign in to comment.