Index: src/string.js |
diff --git a/src/string.js b/src/string.js |
index b39976c51ed8a15332eccabecc2d1c34e26cf962..7d81abbcfda4ecb32f2663a09f5e1ea149d7fe2f 100644 |
--- a/src/string.js |
+++ b/src/string.js |
@@ -229,60 +229,78 @@ function StringReplace(search, replace) { |
} |
var subject = TO_STRING_INLINE(this); |
- // Delegate to one of the regular expression variants if necessary. |
+ // Decision tree for dispatch |
+ // .. regexp search |
+ // .... string replace |
+ // ...... non-global search |
+ // ........ empty string replace |
+ // ........ non-empty string replace (with $-expansion) |
+ // ...... global search |
+ // ........ no need to circumvent last match info override |
+ // ........ need to circument last match info override |
+ // .... function replace |
+ // ...... global search |
+ // ...... non-global search |
+ // .. string search |
+ // .... special case that replaces with one single character |
+ // ...... function replace |
+ // ...... string replace (with $-expansion) |
+ |
if (IS_REGEXP(search)) { |
- // Emulate RegExp.prototype.exec's side effect in step 5, even though |
+ // Emulate RegExp.prototype.exec's side effect in step 5, even if |
// value is discarded. |
ToInteger(search.lastIndex); |
%_Log('regexp', 'regexp-replace,%0r,%1S', [search, subject]); |
- if (IS_SPEC_FUNCTION(replace)) { |
- if (search.global) { |
- return StringReplaceGlobalRegExpWithFunction(subject, search, replace); |
- } else { |
- return StringReplaceNonGlobalRegExpWithFunction(subject, |
- search, |
- replace); |
- } |
- } else { |
- if (lastMatchInfoOverride == null) { |
- var answer = %StringReplaceRegExpWithString(subject, |
- search, |
- TO_STRING_INLINE(replace), |
- lastMatchInfo); |
- if (IS_UNDEFINED(answer)) { // No match. Return subject string. |
- search.lastIndex = 0; |
+ |
+ if (!IS_SPEC_FUNCTION(replace)) { |
+ if (!search.global) { |
+ // Non-global regexp search, string replace. |
+ var match = DoRegExpExec(search, subject, 0); |
+ if (match == null) { |
+ search.lastIndex = 0 |
return subject; |
} |
- if (search.global) search.lastIndex = 0; |
- return answer; |
+ if (replace.length == 0) { |
+ return SubString(subject, 0, match[CAPTURE0]) + |
+ SubString(subject, match[CAPTURE1], subject.length) |
+ } |
+ return ExpandReplacement(TO_STRING_INLINE(replace), |
+ subject, |
+ lastMatchInfo, |
+ SubString(subject, 0, match[CAPTURE0])) + |
+ SubString(subject, match[CAPTURE1], subject.length); |
ulan
2013/02/05 09:30:58
I would reformat by putting the first argument of
Yang
2013/02/05 10:47:32
Not entirely sure what you mean here.
|
+ } |
+ |
+ // Global regexp search, string replace. |
+ search.lastIndex = 0; |
+ if (lastMatchInfoOverride == null) { |
+ return %StringReplaceGlobalRegExpWithString( |
+ subject, search, replace, lastMatchInfo); |
} else { |
// We use this hack to detect whether StringReplaceRegExpWithString |
// found at least one hit. In that case we need to remove any |
// override. |
var saved_subject = lastMatchInfo[LAST_SUBJECT_INDEX]; |
lastMatchInfo[LAST_SUBJECT_INDEX] = 0; |
- var answer = %StringReplaceRegExpWithString(subject, |
- search, |
- TO_STRING_INLINE(replace), |
- lastMatchInfo); |
- if (IS_UNDEFINED(answer)) { // No match. Return subject string. |
- search.lastIndex = 0; |
- lastMatchInfo[LAST_SUBJECT_INDEX] = saved_subject; |
- return subject; |
- } |
+ var answer = %StringReplaceGlobalRegExpWithString( |
+ subject, search, replace, lastMatchInfo); |
if (%_IsSmi(lastMatchInfo[LAST_SUBJECT_INDEX])) { |
lastMatchInfo[LAST_SUBJECT_INDEX] = saved_subject; |
} else { |
lastMatchInfoOverride = null; |
} |
- if (search.global) search.lastIndex = 0; |
return answer; |
} |
} |
+ |
+ if (search.global) { |
+ // Global regexp search, function replace. |
+ return StringReplaceGlobalRegExpWithFunction(subject, search, replace); |
+ } |
+ // Non-global regexp search, function replace. |
+ return StringReplaceNonGlobalRegExpWithFunction(subject, search, replace); |
} |
- // Convert the search argument to a string and search for it. |
- search = TO_STRING_INLINE(search); |
ulan
2013/02/05 09:30:58
Looks like it was remove accidentally.
|
if (search.length == 1 && |
subject.length > 0xFF && |
IS_STRING(replace) && |
@@ -342,6 +360,38 @@ function ExpandReplacement(string, subject, matchInfo, result) { |
} else if (peek == 39) { // $' - suffix |
++position; |
result += SubString(subject, matchInfo[CAPTURE1], subject.length); |
+ } else if (peek >= 48 && peek <= 57) { // between '0' and '9' |
+ // Convert to digit by subtracting '0'. |
+ var scaled_index; |
+ var advance; |
+ if (peek == 48) { // $0... |
+ if (position + 1 == string.length || // ends with '$0', invalid. |
+ %_StringCharCodeAt(string, position + 1) != 49) { // not '$01' |
ulan
2013/02/05 09:30:58
As discussed offline, we should check $02, ... $nn
|
+ scaled_index = 1 << 18; |
+ } else { |
+ scaled_index = 2; // 1 * 2 |
+ advance = 2; |
+ } |
+ } else if (peek == 49 && |
+ position + 1 < string.length && |
+ %_StringCharCodeAt(string, position + 1) == 48) { // $10 |
+ scaled_index = 20; // 10 * 2 |
+ advance = 2; |
+ } else { |
+ scaled_index = (peek - 48) << 1; // index * 2 |
+ advance = 1; |
+ } |
+ |
+ if (NUMBER_OF_CAPTURES(matchInfo) > scaled_index) { |
+ var start = matchInfo[CAPTURE(scaled_index)]; |
+ if (start >= 0) { |
+ result += |
+ SubString(subject, start, matchInfo[CAPTURE(scaled_index + 1)]); |
+ } |
+ position += advance; |
+ } else { |
+ result += '$'; |
+ } |
} else { |
result += '$'; |
} |