1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 11:48:10 +00:00

LibJS: Implement and use the GetSubstitution abstract operation

Used by String.prototype.replace, String.prototype.replaceAll, and
RegExp.prototype [ @@replace ].
This commit is contained in:
Timothy Flynn 2021-07-05 16:16:48 -04:00 committed by Linus Groh
parent 4985d3ef42
commit e0c9f58b0c
5 changed files with 129 additions and 6 deletions

View file

@ -5,6 +5,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/CharacterTypes.h>
#include <AK/Function.h>
#include <AK/Optional.h>
#include <AK/Result.h>
@ -574,4 +575,91 @@ Value canonical_numeric_index_string(GlobalObject& global_object, PropertyName c
return n;
}
// 22.1.3.17.1 GetSubstitution ( matched, str, position, captures, namedCaptures, replacement ), https://tc39.es/ecma262/#sec-getsubstitution
String get_substitution(GlobalObject& global_object, String const& matched, String const& str, size_t position, Vector<Value> const& captures, Value named_captures, Value replacement)
{
auto& vm = global_object.vm();
auto replace_string = replacement.to_string(global_object);
if (vm.exception())
return {};
StringBuilder result;
for (size_t i = 0; i < replace_string.length(); ++i) {
char curr = replace_string[i];
if ((curr != '$') || (i + 1 >= replace_string.length())) {
result.append(curr);
continue;
}
char next = replace_string[i + 1];
if (next == '$') {
result.append(next);
++i;
} else if (next == '&') {
result.append(matched);
++i;
} else if (next == '`') {
result.append(str.substring_view(0, position));
++i;
} else if (next == '\'') {
auto tail_pos = position + matched.length();
if (tail_pos < str.length())
result.append(str.substring_view(tail_pos));
++i;
} else if (is_ascii_digit(next)) {
bool is_two_digits = (i + 2 < replace_string.length()) && is_ascii_digit(replace_string[i + 2]);
auto capture_postition_string = replace_string.substring_view(i + 1, is_two_digits ? 2 : 1);
auto capture_position = capture_postition_string.to_uint();
if (capture_position.has_value() && (*capture_position > 0) && (*capture_position <= captures.size())) {
auto& value = captures[*capture_position - 1];
if (!value.is_undefined()) {
auto value_string = value.to_string(global_object);
if (vm.exception())
return {};
result.append(value_string);
}
i += is_two_digits ? 2 : 1;
} else {
result.append(curr);
}
} else if (next == '<') {
auto start_position = i + 2;
auto end_position = replace_string.find('>', start_position);
if (named_captures.is_undefined() || !end_position.has_value()) {
result.append(curr);
} else {
auto group_name = replace_string.substring(start_position, *end_position - start_position);
auto capture = named_captures.as_object().get(group_name);
if (vm.exception())
return {};
if (!capture.is_undefined()) {
auto capture_string = capture.to_string(global_object);
if (vm.exception())
return {};
result.append(capture_string);
}
i = *end_position;
}
} else {
result.append(curr);
}
}
return result.build();
}
}