|
| 1 | +// Copyright (C) 2023 Richard Gibson. All rights reserved. |
| 2 | +// This code is governed by the BSD license found in the LICENSE file. |
| 3 | +/*--- |
| 4 | +esid: sec-string.prototype.replace |
| 5 | +description: > |
| 6 | + All $n and $nn substrings of the replacement template must be replaced with |
| 7 | + contents of the corresponding regular expression match capture group (if the |
| 8 | + n or nn identifies a valid capture index). |
| 9 | +info: | |
| 10 | + String.prototype.replace ( searchValue, replaceValue ) |
| 11 | +
|
| 12 | + 1. Let O be ? RequireObjectCoercible(this value). |
| 13 | + 2. If searchValue is neither undefined nor null, then |
| 14 | + a. Let replacer be ? GetMethod(searchValue, @@replace). |
| 15 | + b. If replacer is not undefined, then |
| 16 | + i. Return ? Call(replacer, searchValue, « O, replaceValue »). |
| 17 | +
|
| 18 | + RegExp.prototype [ @@replace ] ( string, replaceValue ) |
| 19 | +
|
| 20 | + 15. For each element result of results, do |
| 21 | + a. Let resultLength be ? LengthOfArrayLike(result). |
| 22 | + b. Let nCaptures be max(resultLength - 1, 0). |
| 23 | + c. Let matched be ? ToString(? Get(result, "0")). |
| 24 | + d. Let matchLength be the length of matched. |
| 25 | + e. Let position be ? ToIntegerOrInfinity(? Get(result, "index")). |
| 26 | + f. Set position to the result of clamping position between 0 and lengthS. |
| 27 | + g. Let captures be a new empty List. |
| 28 | + h. Let n be 1. |
| 29 | + i. Repeat, while n ≤ nCaptures, |
| 30 | + ... |
| 31 | + iii. Append capN to captures. |
| 32 | + iv. NOTE: When n = 1, the preceding step puts the first element into captures (at index 0). More generally, the nth capture (the characters captured by the nth set of capturing parentheses) is at captures[n - 1]. |
| 33 | + v. Set n to n + 1. |
| 34 | + j. Let namedCaptures be ? Get(result, "groups"). |
| 35 | + k. If functionalReplace is true, then |
| 36 | + ... |
| 37 | + l. Else, |
| 38 | + i. If namedCaptures is not undefined, then |
| 39 | + 1. Set namedCaptures to ? ToObject(namedCaptures). |
| 40 | + ii. Let replacement be ? GetSubstitution(matched, S, position, captures, namedCaptures, replaceValue). |
| 41 | + ... |
| 42 | + 16. If nextSourcePosition ≥ lengthS, return accumulatedResult. |
| 43 | + 17. Return the string-concatenation of accumulatedResult and the substring of S from nextSourcePosition. |
| 44 | +
|
| 45 | + GetSubstitution ( matched, str, position, captures, namedCaptures, replacementTemplate ) |
| 46 | +
|
| 47 | + 1. Let stringLength be the length of str. |
| 48 | + 2. Assert: position ≤ stringLength. |
| 49 | + 3. Let result be the empty String. |
| 50 | + 4. Let templateRemainder be replacementTemplate. |
| 51 | + 5. Repeat, while templateRemainder is not the empty String, |
| 52 | + a. NOTE: The following steps isolate ref (a prefix of templateRemainder), determine refReplacement (its replacement), and then append that replacement to result. |
| 53 | + ... |
| 54 | + f. Else if templateRemainder starts with "$" followed by 1 or more decimal digits, then |
| 55 | + i. If templateRemainder starts with "$" followed by 2 or more decimal digits, let digitCount be 2. Otherwise, let digitCount be 1. |
| 56 | + ii. Let digits be the substring of templateRemainder from 1 to 1 + digitCount. |
| 57 | + iii. Let index be ℝ(StringToNumber(digits)). |
| 58 | + iv. Assert: 0 ≤ index ≤ 99. |
| 59 | + v. Let captureLen be the number of elements in captures. |
| 60 | + vi. If index > captureLen and digitCount > 1, then |
| 61 | + 1. NOTE: When a two-digit replacement pattern specifies an index exceeding the count of capturing groups, it is treated as a one-digit replacement pattern followed by a literal digit. |
| 62 | + 2. Set digitCount to 1. |
| 63 | + 3. Set digits to the substring of digits from 0 to 1. |
| 64 | + 4. Set index to ℝ(StringToNumber(digits)). |
| 65 | + vii. Let ref be the substring of templateRemainder from 0 to 1 + digitCount. |
| 66 | + viii. If 1 ≤ index ≤ captureLen, then |
| 67 | + 1. Let capture be captures[index - 1]. |
| 68 | + 2. If capture is undefined, then |
| 69 | + a. Let refReplacement be the empty String. |
| 70 | + 3. Else, |
| 71 | + a. Let refReplacement be capture. |
| 72 | + ix. Else, |
| 73 | + 1. Let refReplacement be ref. |
| 74 | + ... |
| 75 | + i. Let refLength be the length of ref. |
| 76 | + j. Set templateRemainder to the substring of templateRemainder from refLength. |
| 77 | + k. Set result to the string-concatenation of result and refReplacement. |
| 78 | + 6. Return result. |
| 79 | +---*/ |
| 80 | + |
| 81 | +var str = 'foo-x-bar'; |
| 82 | + |
| 83 | +var x = 'x'; |
| 84 | +var re0 = /x/; |
| 85 | +var re1 = /(x)/; |
| 86 | +var re1x = /(x)($^)?/; |
| 87 | +var re10 = /((((((((((x))))))))))/; |
| 88 | + |
| 89 | +assert.sameValue(str.replace(x, '|$0|'), 'foo-|$0|-bar', |
| 90 | + '`$0` is not a capture index for string "' + x + '"'); |
| 91 | +assert.sameValue(str.replace(re0, '|$0|'), 'foo-|$0|-bar', |
| 92 | + '`$0` is not a capture index in ' + String(re0)); |
| 93 | +assert.sameValue(str.replace(re1, '|$0|'), 'foo-|$0|-bar', |
| 94 | + '`$0` is not a capture index in ' + String(re1)); |
| 95 | +assert.sameValue(str.replace(re1x, '|$0|'), 'foo-|$0|-bar', |
| 96 | + '`$0` is not a capture index in ' + String(re1x)); |
| 97 | +assert.sameValue(str.replace(re10, '|$0|'), 'foo-|$0|-bar', |
| 98 | + '`$0` is not a capture index in ' + String(re10)); |
| 99 | + |
| 100 | +assert.sameValue(str.replace(x, '|$00|'), 'foo-|$00|-bar', |
| 101 | + '`$00` is not a capture index for string "' + x + '"'); |
| 102 | +assert.sameValue(str.replace(re0, '|$00|'), 'foo-|$00|-bar', |
| 103 | + '`$00` is not a capture index in ' + String(re0)); |
| 104 | +assert.sameValue(str.replace(re1, '|$00|'), 'foo-|$00|-bar', |
| 105 | + '`$00` is not a capture index in ' + String(re1)); |
| 106 | +assert.sameValue(str.replace(re1x, '|$00|'), 'foo-|$00|-bar', |
| 107 | + '`$00` is not a capture index in ' + String(re1x)); |
| 108 | +assert.sameValue(str.replace(re10, '|$00|'), 'foo-|$00|-bar', |
| 109 | + '`$00` is not a capture index in ' + String(re10)); |
| 110 | + |
| 111 | +assert.sameValue(str.replace(x, '|$000|'), 'foo-|$000|-bar', |
| 112 | + '`$00` before `0` is not a capture index for string "' + x + '"'); |
| 113 | +assert.sameValue(str.replace(re0, '|$000|'), 'foo-|$000|-bar', |
| 114 | + '`$00` before `0` is not a capture index in ' + String(re0)); |
| 115 | +assert.sameValue(str.replace(re1, '|$000|'), 'foo-|$000|-bar', |
| 116 | + '`$00` before `0` is not a capture index in ' + String(re1)); |
| 117 | +assert.sameValue(str.replace(re1x, '|$000|'), 'foo-|$000|-bar', |
| 118 | + '`$00` before `0` is not a capture index in ' + String(re1x)); |
| 119 | +assert.sameValue(str.replace(re10, '|$000|'), 'foo-|$000|-bar', |
| 120 | + '`$00` before `0` is not a capture index in ' + String(re10)); |
| 121 | + |
| 122 | +assert.sameValue(str.replace(x, '|$1|'), 'foo-|$1|-bar', |
| 123 | + '`$1` is not a capture index for string "' + x + '"'); |
| 124 | +assert.sameValue(str.replace(re0, '|$1|'), 'foo-|$1|-bar', |
| 125 | + '`$1` is not a capture index in ' + String(re0)); |
| 126 | +assert.sameValue(str.replace(re1, '|$1|'), 'foo-|x|-bar', |
| 127 | + '`$1` is a capture index in ' + String(re1)); |
| 128 | +assert.sameValue(str.replace(re1x, '|$1|'), 'foo-|x|-bar', |
| 129 | + '`$1` is a capture index in ' + String(re1x)); |
| 130 | +assert.sameValue(str.replace(re10, '|$1|'), 'foo-|x|-bar', |
| 131 | + '`$1` is a capture index in ' + String(re10)); |
| 132 | + |
| 133 | +assert.sameValue(str.replace(x, '|$01|'), 'foo-|$01|-bar', |
| 134 | + '`$01` is not a capture index for string "' + x + '"'); |
| 135 | +assert.sameValue(str.replace(re0, '|$01|'), 'foo-|$01|-bar', |
| 136 | + '`$01` is not a capture index in ' + String(re0)); |
| 137 | +assert.sameValue(str.replace(re1, '|$01|'), 'foo-|x|-bar', |
| 138 | + '`$01` is a capture index in ' + String(re1)); |
| 139 | +assert.sameValue(str.replace(re1x, '|$01|'), 'foo-|x|-bar', |
| 140 | + '`$01` is a capture index in ' + String(re1x)); |
| 141 | +assert.sameValue(str.replace(re10, '|$01|'), 'foo-|x|-bar', |
| 142 | + '`$01` is a capture index in ' + String(re10)); |
| 143 | + |
| 144 | +assert.sameValue(str.replace(x, '|$010|'), 'foo-|$010|-bar', |
| 145 | + '`$01` before `0` is not a capture index for string "' + x + '"'); |
| 146 | +assert.sameValue(str.replace(re0, '|$010|'), 'foo-|$010|-bar', |
| 147 | + '`$01` before `0` is not a capture index in ' + String(re0)); |
| 148 | +assert.sameValue(str.replace(re1, '|$010|'), 'foo-|x0|-bar', |
| 149 | + '`$01` before `0` is a capture index in ' + String(re1)); |
| 150 | +assert.sameValue(str.replace(re1x, '|$010|'), 'foo-|x0|-bar', |
| 151 | + '`$01` before `0` is a capture index in ' + String(re1x)); |
| 152 | +assert.sameValue(str.replace(re10, '|$010|'), 'foo-|x0|-bar', |
| 153 | + '`$01` before `0` is a capture index in ' + String(re10)); |
| 154 | + |
| 155 | +assert.sameValue(str.replace(x, '|$2|'), 'foo-|$2|-bar', |
| 156 | + '`$2` is not a capture index for string "' + x + '"'); |
| 157 | +assert.sameValue(str.replace(re0, '|$2|'), 'foo-|$2|-bar', |
| 158 | + '`$2` is not a capture index in ' + String(re0)); |
| 159 | +assert.sameValue(str.replace(re1, '|$2|'), 'foo-|$2|-bar', |
| 160 | + '`$2` is not a capture index in ' + String(re1)); |
| 161 | +assert.sameValue(str.replace(re1x, '|$2|'), 'foo-||-bar', |
| 162 | + '`$2` is a failed capture index in ' + String(re1x)); |
| 163 | +assert.sameValue(str.replace(re10, '|$2|'), 'foo-|x|-bar', |
| 164 | + '`$2` is a capture index in ' + String(re10)); |
| 165 | + |
| 166 | +assert.sameValue(str.replace(x, '|$02|'), 'foo-|$02|-bar', |
| 167 | + '`$02` is not a capture index for string "' + x + '"'); |
| 168 | +assert.sameValue(str.replace(re0, '|$02|'), 'foo-|$02|-bar', |
| 169 | + '`$02` is not a capture index in ' + String(re0)); |
| 170 | +assert.sameValue(str.replace(re1, '|$02|'), 'foo-|$02|-bar', |
| 171 | + '`$02` is not a capture index in ' + String(re1)); |
| 172 | +assert.sameValue(str.replace(re1x, '|$02|'), 'foo-||-bar', |
| 173 | + '`$02` is a failed capture index in ' + String(re1x)); |
| 174 | +assert.sameValue(str.replace(re10, '|$02|'), 'foo-|x|-bar', |
| 175 | + '`$02` is a capture index in ' + String(re10)); |
| 176 | + |
| 177 | +assert.sameValue(str.replace(x, '|$020|'), 'foo-|$020|-bar', |
| 178 | + '`$02` before `0` is not a capture index for string "' + x + '"'); |
| 179 | +assert.sameValue(str.replace(re0, '|$020|'), 'foo-|$020|-bar', |
| 180 | + '`$02` before `0` is not a capture index in ' + String(re0)); |
| 181 | +assert.sameValue(str.replace(re1, '|$020|'), 'foo-|$020|-bar', |
| 182 | + '`$02` before `0` is not a capture index in ' + String(re1)); |
| 183 | +assert.sameValue(str.replace(re1x, '|$020|'), 'foo-|0|-bar', |
| 184 | + '`$02` before `0` is a failed capture index in ' + String(re1x)); |
| 185 | +assert.sameValue(str.replace(re10, '|$020|'), 'foo-|x0|-bar', |
| 186 | + '`$02` before `0` is a capture index in ' + String(re10)); |
| 187 | + |
| 188 | +assert.sameValue(str.replace(x, '|$10|'), 'foo-|$10|-bar', |
| 189 | + '`$10` is not a capture index (nor is `$1`) for string "' + x + '"'); |
| 190 | +assert.sameValue(str.replace(re0, '|$10|'), 'foo-|$10|-bar', |
| 191 | + '`$10` is not a capture index (nor is `$1`) in ' + String(re0)); |
| 192 | +assert.sameValue(str.replace(re1, '|$10|'), 'foo-|x0|-bar', |
| 193 | + '`$10` is not a capture index (but `$1` is) in ' + String(re1)); |
| 194 | +assert.sameValue(str.replace(re1x, '|$10|'), 'foo-|x0|-bar', |
| 195 | + '`$10` is not a capture index (but `$1` is) in ' + String(re1x)); |
| 196 | +assert.sameValue(str.replace(re10, '|$10|'), 'foo-|x|-bar', |
| 197 | + '`$10` is a capture index in ' + String(re10)); |
| 198 | + |
| 199 | +assert.sameValue(str.replace(x, '|$100|'), 'foo-|$100|-bar', |
| 200 | + '`$10` before `0` is not a capture index (nor is `$1`) for string "' + x + '"'); |
| 201 | +assert.sameValue(str.replace(re0, '|$100|'), 'foo-|$100|-bar', |
| 202 | + '`$10` before `0` is not a capture index (nor is `$1`) in ' + String(re0)); |
| 203 | +assert.sameValue(str.replace(re1, '|$100|'), 'foo-|x00|-bar', |
| 204 | + '`$10` before `0` is not a capture index (but `$1` is) in ' + String(re1)); |
| 205 | +assert.sameValue(str.replace(re1x, '|$100|'), 'foo-|x00|-bar', |
| 206 | + '`$10` before `0` is not a capture index (but `$1` is) in ' + String(re1x)); |
| 207 | +assert.sameValue(str.replace(re10, '|$100|'), 'foo-|x0|-bar', |
| 208 | + '`$10` before `0` is a capture index in ' + String(re10)); |
| 209 | + |
| 210 | +assert.sameValue(str.replace(x, '|$20|'), 'foo-|$20|-bar', |
| 211 | + '`$20` is not a capture index (nor is `$2`) for string "' + x + '"'); |
| 212 | +assert.sameValue(str.replace(re0, '|$20|'), 'foo-|$20|-bar', |
| 213 | + '`$20` is not a capture index (nor is `$2`) in ' + String(re0)); |
| 214 | +assert.sameValue(str.replace(re1, '|$20|'), 'foo-|$20|-bar', |
| 215 | + '`$20` is not a capture index (nor is `$2`) in ' + String(re1)); |
| 216 | +assert.sameValue(str.replace(re1x, '|$20|'), 'foo-|0|-bar', |
| 217 | + '`$20` is not a capture index (but `$2` is a failed capture index) in ' + String(re1x)); |
| 218 | +assert.sameValue(str.replace(re10, '|$20|'), 'foo-|x0|-bar', |
| 219 | + '`$20` is not a capture index (but `$2` is) in ' + String(re10)); |
| 220 | + |
| 221 | +assert.sameValue(str.replace(x, '|$200|'), 'foo-|$200|-bar', |
| 222 | + '`$20` before `0` is not a capture index (nor is `$2`) for string "' + x + '"'); |
| 223 | +assert.sameValue(str.replace(re0, '|$200|'), 'foo-|$200|-bar', |
| 224 | + '`$20` before `0` is not a capture index (nor is `$2`) in ' + String(re0)); |
| 225 | +assert.sameValue(str.replace(re1, '|$200|'), 'foo-|$200|-bar', |
| 226 | + '`$20` before `0` is not a capture index (nor is `$2`) in ' + String(re1)); |
| 227 | +assert.sameValue(str.replace(re1x, '|$200|'), 'foo-|00|-bar', |
| 228 | + '`$20` before `0` is not a capture index (but `$2` is a failed capture index) in ' + String(re1x)); |
| 229 | +assert.sameValue(str.replace(re10, '|$200|'), 'foo-|x00|-bar', |
| 230 | + '`$20` before `0` is not a capture index (but `$2` is) in ' + String(re10)); |
0 commit comments