[regexp/string] Merge ExpandReplacement and GetSubstitution
R=littledan@chromium.org BUG=v8:5339 Review-Url: https://codereview.chromium.org/2332333002 Cr-Commit-Position: refs/heads/master@{#39528}
This commit is contained in:
parent
5e113c7241
commit
5ab3874559
@ -11,7 +11,6 @@
|
||||
// -------------------------------------------------------------------
|
||||
// Imports
|
||||
|
||||
var ExpandReplacement;
|
||||
var GlobalArray = global.Array;
|
||||
var GlobalObject = global.Object;
|
||||
var GlobalRegExp = global.RegExp;
|
||||
@ -28,7 +27,6 @@ var splitSymbol = utils.ImportNow("split_symbol");
|
||||
var SpeciesConstructor;
|
||||
|
||||
utils.Import(function(from) {
|
||||
ExpandReplacement = from.ExpandReplacement;
|
||||
MaxSimple = from.MaxSimple;
|
||||
MinSimple = from.MinSimple;
|
||||
SpeciesConstructor = from.SpeciesConstructor;
|
||||
@ -565,6 +563,31 @@ function StringReplaceNonGlobalRegExpWithFunction(subject, regexp, replace) {
|
||||
return result + %_SubString(subject, endOfMatch, subject.length);
|
||||
}
|
||||
|
||||
// Wraps access to matchInfo's captures into a format understood by
|
||||
// GetSubstitution.
|
||||
function MatchInfoCaptureWrapper(matches, subject) {
|
||||
this.length = NUMBER_OF_CAPTURES(matches) >> 1;
|
||||
this.match = matches;
|
||||
this.subject = subject;
|
||||
}
|
||||
|
||||
MatchInfoCaptureWrapper.prototype.at = function(ix) {
|
||||
const match = this.match;
|
||||
const start = match[CAPTURE(ix << 1)];
|
||||
if (start < 0) return UNDEFINED;
|
||||
return %_SubString(this.subject, start, match[CAPTURE((ix << 1) + 1)]);
|
||||
};
|
||||
%SetForceInlineFlag(MatchInfoCaptureWrapper.prototype.at);
|
||||
|
||||
function ArrayCaptureWrapper(array) {
|
||||
this.length = array.length;
|
||||
this.array = array;
|
||||
}
|
||||
|
||||
ArrayCaptureWrapper.prototype.at = function(ix) {
|
||||
return this.array[ix];
|
||||
};
|
||||
%SetForceInlineFlag(ArrayCaptureWrapper.prototype.at);
|
||||
|
||||
function RegExpReplace(string, replace) {
|
||||
if (!IS_REGEXP(this)) {
|
||||
@ -588,9 +611,17 @@ function RegExpReplace(string, replace) {
|
||||
return %_SubString(subject, 0, match[CAPTURE0]) +
|
||||
%_SubString(subject, match[CAPTURE1], subject.length)
|
||||
}
|
||||
return ExpandReplacement(replace, subject, RegExpLastMatchInfo,
|
||||
%_SubString(subject, 0, match[CAPTURE0])) +
|
||||
%_SubString(subject, match[CAPTURE1], subject.length);
|
||||
const captures = new MatchInfoCaptureWrapper(match, subject);
|
||||
const start = match[CAPTURE0];
|
||||
const end = match[CAPTURE1];
|
||||
|
||||
const prefix = %_SubString(subject, 0, start);
|
||||
const matched = %_SubString(subject, start, end);
|
||||
const suffix = %_SubString(subject, end, subject.length);
|
||||
|
||||
return prefix +
|
||||
GetSubstitution(matched, subject, start, captures, replace) +
|
||||
suffix;
|
||||
}
|
||||
|
||||
// Global regexp search, string replace.
|
||||
@ -612,8 +643,6 @@ function RegExpReplace(string, replace) {
|
||||
// GetSubstitution(matched, str, position, captures, replacement)
|
||||
// Expand the $-expressions in the string and return a new string with
|
||||
// the result.
|
||||
// TODO(littledan): Call this function from String.prototype.replace instead
|
||||
// of the very similar ExpandReplacement in src/js/string.js
|
||||
function GetSubstitution(matched, string, position, captures, replacement) {
|
||||
var matchLength = matched.length;
|
||||
var stringLength = string.length;
|
||||
@ -662,7 +691,7 @@ function GetSubstitution(matched, string, position, captures, replacement) {
|
||||
}
|
||||
}
|
||||
if (scaledIndex != 0 && scaledIndex < capturesLength) {
|
||||
var capture = captures[scaledIndex];
|
||||
var capture = captures.at(scaledIndex);
|
||||
if (!IS_UNDEFINED(capture)) result += capture;
|
||||
pos += advance;
|
||||
} else {
|
||||
@ -786,7 +815,8 @@ function RegExpSubclassReplace(string, replace) {
|
||||
replacement = %reflect_apply(replace, UNDEFINED, parameters, 0,
|
||||
parameters.length);
|
||||
} else {
|
||||
replacement = GetSubstitution(matched, string, position, captures,
|
||||
const capturesWrapper = new ArrayCaptureWrapper(captures);
|
||||
replacement = GetSubstitution(matched, string, position, capturesWrapper,
|
||||
replace);
|
||||
}
|
||||
if (position >= nextSourcePosition) {
|
||||
@ -1095,6 +1125,7 @@ function InternalRegExpReplace(regexp, subject, replacement) {
|
||||
// Exports
|
||||
|
||||
utils.Export(function(to) {
|
||||
to.GetSubstitution = GetSubstitution;
|
||||
to.InternalRegExpMatch = InternalRegExpMatch;
|
||||
to.InternalRegExpReplace = InternalRegExpReplace;
|
||||
to.IsRegExp = IsRegExp;
|
||||
|
106
src/js/string.js
106
src/js/string.js
@ -10,6 +10,7 @@
|
||||
// Imports
|
||||
|
||||
var ArrayJoin;
|
||||
var GetSubstitution;
|
||||
var GlobalRegExp = global.RegExp;
|
||||
var GlobalString = global.String;
|
||||
var IsRegExp;
|
||||
@ -23,6 +24,7 @@ var splitSymbol = utils.ImportNow("split_symbol");
|
||||
|
||||
utils.Import(function(from) {
|
||||
ArrayJoin = from.ArrayJoin;
|
||||
GetSubstitution = from.GetSubstitution;
|
||||
IsRegExp = from.IsRegExp;
|
||||
MaxSimple = from.MaxSimple;
|
||||
MinSimple = from.MinSimple;
|
||||
@ -79,14 +81,6 @@ function StringMatchJS(pattern) {
|
||||
}
|
||||
|
||||
|
||||
// This has the same size as the RegExpLastMatchInfo array, and can be used
|
||||
// for functions that expect that structure to be returned. It is used when
|
||||
// the needle is a string rather than a regexp. In this case we can't update
|
||||
// lastMatchArray without erroneously affecting the properties on the global
|
||||
// RegExp object.
|
||||
var reusableMatchInfo = [2, "", "", -1, -1];
|
||||
|
||||
|
||||
// ES6, section 21.1.3.14
|
||||
function StringReplace(search, replace) {
|
||||
CHECK_OBJECT_COERCIBLE(this, "String.prototype.replace");
|
||||
@ -138,101 +132,18 @@ function StringReplace(search, replace) {
|
||||
if (IS_CALLABLE(replace)) {
|
||||
result += replace(search, start, subject);
|
||||
} else {
|
||||
reusableMatchInfo[CAPTURE0] = start;
|
||||
reusableMatchInfo[CAPTURE1] = end;
|
||||
result = ExpandReplacement(TO_STRING(replace),
|
||||
subject,
|
||||
reusableMatchInfo,
|
||||
result);
|
||||
// In this case, we don't have any capture groups and can get away with
|
||||
// faking the captures object by simply setting its length to 1.
|
||||
const captures = { length: 1 };
|
||||
const matched = %_SubString(subject, start, end);
|
||||
result += GetSubstitution(matched, subject, start, captures,
|
||||
TO_STRING(replace));
|
||||
}
|
||||
|
||||
return result + %_SubString(subject, end, subject.length);
|
||||
}
|
||||
|
||||
|
||||
// Expand the $-expressions in the string and return a new string with
|
||||
// the result.
|
||||
function ExpandReplacement(string, subject, matchInfo, result) {
|
||||
var length = string.length;
|
||||
var next = %StringIndexOf(string, '$', 0);
|
||||
if (next < 0) {
|
||||
if (length > 0) result += string;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (next > 0) result += %_SubString(string, 0, next);
|
||||
|
||||
while (true) {
|
||||
var expansion = '$';
|
||||
var position = next + 1;
|
||||
if (position < length) {
|
||||
var peek = %_StringCharCodeAt(string, position);
|
||||
if (peek == 36) { // $$
|
||||
++position;
|
||||
result += '$';
|
||||
} else if (peek == 38) { // $& - match
|
||||
++position;
|
||||
result +=
|
||||
%_SubString(subject, matchInfo[CAPTURE0], matchInfo[CAPTURE1]);
|
||||
} else if (peek == 96) { // $` - prefix
|
||||
++position;
|
||||
result += %_SubString(subject, 0, matchInfo[CAPTURE0]);
|
||||
} else if (peek == 39) { // $' - suffix
|
||||
++position;
|
||||
result += %_SubString(subject, matchInfo[CAPTURE1], subject.length);
|
||||
} else if (peek >= 48 && peek <= 57) {
|
||||
// Valid indices are $1 .. $9, $01 .. $09 and $10 .. $99
|
||||
var scaled_index = (peek - 48) << 1;
|
||||
var advance = 1;
|
||||
var number_of_captures = NUMBER_OF_CAPTURES(matchInfo);
|
||||
if (position + 1 < string.length) {
|
||||
var next = %_StringCharCodeAt(string, position + 1);
|
||||
if (next >= 48 && next <= 57) {
|
||||
var new_scaled_index = scaled_index * 10 + ((next - 48) << 1);
|
||||
if (new_scaled_index < number_of_captures) {
|
||||
scaled_index = new_scaled_index;
|
||||
advance = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (scaled_index != 0 && scaled_index < number_of_captures) {
|
||||
var start = matchInfo[CAPTURE(scaled_index)];
|
||||
if (start >= 0) {
|
||||
result +=
|
||||
%_SubString(subject, start, matchInfo[CAPTURE(scaled_index + 1)]);
|
||||
}
|
||||
position += advance;
|
||||
} else {
|
||||
result += '$';
|
||||
}
|
||||
} else {
|
||||
result += '$';
|
||||
}
|
||||
} else {
|
||||
result += '$';
|
||||
}
|
||||
|
||||
// Go the the next $ in the string.
|
||||
next = %StringIndexOf(string, '$', position);
|
||||
|
||||
// Return if there are no more $ characters in the string. If we
|
||||
// haven't reached the end, we need to append the suffix.
|
||||
if (next < 0) {
|
||||
if (position < length) {
|
||||
result += %_SubString(string, position, length);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Append substring between the previous and the next $ character.
|
||||
if (next > position) {
|
||||
result += %_SubString(string, position, next);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// ES6 21.1.3.15.
|
||||
function StringSearch(pattern) {
|
||||
CHECK_OBJECT_COERCIBLE(this, "String.prototype.search");
|
||||
@ -707,7 +618,6 @@ utils.InstallFunctions(GlobalString.prototype, DONT_ENUM, [
|
||||
// Exports
|
||||
|
||||
utils.Export(function(to) {
|
||||
to.ExpandReplacement = ExpandReplacement;
|
||||
to.StringIndexOf = StringIndexOf;
|
||||
to.StringMatch = StringMatchJS;
|
||||
to.StringReplace = StringReplace;
|
||||
|
Loading…
Reference in New Issue
Block a user