1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-26 05:27:46 +00:00

LibJS: Implement global RegExp.prototype.match

Also rename the 'rx' variable to 'regexp_object' to match other RegExp
methods.
This commit is contained in:
Timothy Flynn 2021-07-07 14:11:46 -04:00 committed by Linus Groh
parent b6b5adb47d
commit 2d0589f93c
2 changed files with 73 additions and 17 deletions

View file

@ -82,6 +82,24 @@ static String escape_regexp_pattern(const RegExpObject& regexp_object)
return pattern;
}
static void increment_last_index(GlobalObject& global_object, Object& regexp_object)
{
auto& vm = global_object.vm();
auto last_index_value = regexp_object.get(vm.names.lastIndex);
if (vm.exception())
return;
auto last_index = last_index_value.to_length(global_object);
if (vm.exception())
return;
// FIXME: Implement AdvanceStringIndex to take Unicode code points into account - https://tc39.es/ecma262/#sec-advancestringindex
// Once implemented, step (8a) of the @@replace algorithm must also be implemented.
++last_index;
regexp_object.set(vm.names.lastIndex, Value(last_index), true);
}
static RegexResult do_match(const Regex<ECMA262>& re, const StringView& subject)
{
auto result = re.match(subject);
@ -302,28 +320,68 @@ JS_DEFINE_NATIVE_FUNCTION(RegExpPrototype::to_string)
// 22.2.5.7 RegExp.prototype [ @@match ] ( string ), https://tc39.es/ecma262/#sec-regexp.prototype-@@match
JS_DEFINE_NATIVE_FUNCTION(RegExpPrototype::symbol_match)
{
auto* rx = regexp_object_from(vm, global_object);
if (!rx)
auto* regexp_object = regexp_object_from(vm, global_object);
if (!regexp_object)
return {};
auto s = vm.argument(0).to_string(global_object);
if (vm.exception())
return {};
auto global_value = rx->get(vm.names.global);
auto global_value = regexp_object->get(vm.names.global);
if (vm.exception())
return {};
bool global = global_value.to_boolean();
if (!global) {
auto result = regexp_exec(global_object, *rx, s);
auto result = regexp_exec(global_object, *regexp_object, s);
if (vm.exception())
return {};
return result;
}
// FIXME: This should exec the RegExp repeatedly while updating "lastIndex"
auto result = regexp_exec(global_object, *rx, s);
regexp_object->set(vm.names.lastIndex, Value(0), true);
if (vm.exception())
return {};
return result;
auto* array = Array::create(global_object, 0);
if (vm.exception())
return {};
size_t n = 0;
while (true) {
auto result = regexp_exec(global_object, *regexp_object, s);
if (vm.exception())
return {};
if (result.is_null()) {
if (n == 0)
return js_null();
return array;
}
auto* result_object = result.to_object(global_object);
if (!result_object)
return {};
auto match_object = result_object->get(0);
if (vm.exception())
return {};
auto match_str = match_object.to_string(global_object);
if (vm.exception())
return {};
array->create_data_property_or_throw(n, js_string(vm, match_str));
if (vm.exception())
return {};
if (match_str.is_empty()) {
increment_last_index(global_object, *regexp_object);
if (vm.exception())
return {};
}
++n;
}
}
// 22.2.5.10 RegExp.prototype [ @@replace ] ( string, replaceValue ), https://tc39.es/ecma262/#sec-regexp.prototype-@@replace
@ -380,20 +438,12 @@ JS_DEFINE_NATIVE_FUNCTION(RegExpPrototype::symbol_replace)
auto match_object = result_object->get(0);
if (vm.exception())
return {};
String match_str = match_object.to_string(global_object);
if (vm.exception())
return {};
if (match_str.is_empty()) {
// FIXME: Implement AdvanceStringIndex to take Unicode code points into account - https://tc39.es/ecma262/#sec-advancestringindex
// Once implemented, step (8a) of the @@replace algorithm must also be implemented.
auto last_index = regexp_object->get(vm.names.lastIndex);
if (vm.exception())
return {};
auto this_index = last_index.to_length(global_object);
if (vm.exception())
return {};
regexp_object->set(vm.names.lastIndex, Value(this_index + 1), true);
increment_last_index(global_object, *regexp_object);
if (vm.exception())
return {};
}

View file

@ -3,6 +3,12 @@ test("basic functionality", () => {
expect("hello friends".match(/hello/)).not.toBeNull();
expect("hello friends".match(/enemies/)).toBeNull();
expect("aaa".match(/a/)).toEqual(["a"]);
expect("aaa".match(/a/g)).toEqual(["a", "a", "a"]);
expect("aaa".match(/b/)).toBeNull();
expect("aaa".match(/b/g)).toBeNull();
});
test("override exec with function", () => {