mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 16:47:36 +00:00
LibJS: Implement `RegExpPrototype::exec()'
This implements *only* the builtin exec() function.
This commit is contained in:
parent
8ba273a2f3
commit
210a3db44d
4 changed files with 114 additions and 0 deletions
|
@ -91,6 +91,7 @@ namespace JS {
|
|||
P(enumerable) \
|
||||
P(error) \
|
||||
P(every) \
|
||||
P(exec) \
|
||||
P(exp) \
|
||||
P(expm1) \
|
||||
P(fill) \
|
||||
|
@ -126,12 +127,15 @@ namespace JS {
|
|||
P(getUTCSeconds) \
|
||||
P(global) \
|
||||
P(globalThis) \
|
||||
P(groups) \
|
||||
P(has) \
|
||||
P(hasOwnProperty) \
|
||||
P(ignoreCase) \
|
||||
P(includes) \
|
||||
P(index) \
|
||||
P(indexOf) \
|
||||
P(info) \
|
||||
P(input) \
|
||||
P(is) \
|
||||
P(isArray) \
|
||||
P(isExtensible) \
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
*/
|
||||
|
||||
#include <AK/Function.h>
|
||||
#include <LibJS/Runtime/Array.h>
|
||||
#include <LibJS/Runtime/Error.h>
|
||||
#include <LibJS/Runtime/GlobalObject.h>
|
||||
#include <LibJS/Runtime/RegExpObject.h>
|
||||
|
@ -44,6 +45,7 @@ void RegExpPrototype::initialize(GlobalObject& global_object)
|
|||
u8 attr = Attribute::Writable | Attribute::Configurable;
|
||||
define_native_function(vm.names.toString, to_string, 0, attr);
|
||||
define_native_function(vm.names.test, test, 1, attr);
|
||||
define_native_function(vm.names.exec, exec, 1, attr);
|
||||
|
||||
u8 readable_attr = Attribute::Configurable;
|
||||
define_native_property(vm.names.dotAll, dot_all, nullptr, readable_attr);
|
||||
|
@ -170,6 +172,55 @@ RegexResult RegExpPrototype::do_match(const Regex<ECMA262>& re, const StringView
|
|||
return result;
|
||||
}
|
||||
|
||||
JS_DEFINE_NATIVE_FUNCTION(RegExpPrototype::exec)
|
||||
{
|
||||
// FIXME: This should try using dynamic properties for 'lastIndex',
|
||||
// and internal slots [[RegExpMatcher]], [[OriginalFlags]], etc.
|
||||
auto regexp_object = regexp_object_from(vm, global_object);
|
||||
if (!regexp_object)
|
||||
return {};
|
||||
|
||||
auto str = vm.argument(0).to_string(global_object);
|
||||
if (vm.exception())
|
||||
return {};
|
||||
|
||||
StringView str_to_match = str;
|
||||
|
||||
// RegExps without "global" and "sticky" always start at offset 0.
|
||||
if (!regexp_object->regex().options().has_flag_set((ECMAScriptFlags)regex::AllFlags::Internal_Stateful))
|
||||
regexp_object->regex().start_offset = 0;
|
||||
|
||||
auto result = do_match(regexp_object->regex(), str_to_match);
|
||||
if (!result.success)
|
||||
return js_null();
|
||||
|
||||
auto& match = result.matches[0];
|
||||
|
||||
// FIXME: Do code point index correction if the Unicode flag is set.
|
||||
auto* array = Array::create(global_object);
|
||||
array->indexed_properties().set_array_like_size(result.n_capture_groups + 1);
|
||||
array->define_property(vm.names.index, Value((i32)match.column));
|
||||
array->define_property(vm.names.input, js_string(vm, str));
|
||||
array->indexed_properties().put(array, 0, js_string(vm, match.view.to_string()));
|
||||
|
||||
for (size_t i = 0; i < result.n_capture_groups; ++i) {
|
||||
auto& capture = result.capture_group_matches[0][i];
|
||||
array->indexed_properties().put(array, i + 1, js_string(vm, capture.view.to_string()));
|
||||
}
|
||||
|
||||
Value groups = js_undefined();
|
||||
if (result.n_named_capture_groups > 0) {
|
||||
auto groups_object = create_empty(global_object);
|
||||
for (auto& entry : result.named_capture_group_matches[0])
|
||||
groups_object->define_property(entry.key, js_string(vm, entry.value.view.to_string()));
|
||||
groups = move(groups_object);
|
||||
}
|
||||
|
||||
array->define_property(vm.names.groups, groups);
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
JS_DEFINE_NATIVE_FUNCTION(RegExpPrototype::test)
|
||||
{
|
||||
// FIXME: This should try using dynamic properties for 'exec' first,
|
||||
|
|
|
@ -50,6 +50,7 @@ private:
|
|||
JS_DECLARE_NATIVE_GETTER(sticky);
|
||||
JS_DECLARE_NATIVE_GETTER(unicode);
|
||||
|
||||
JS_DECLARE_NATIVE_FUNCTION(exec);
|
||||
JS_DECLARE_NATIVE_FUNCTION(test);
|
||||
JS_DECLARE_NATIVE_FUNCTION(to_string);
|
||||
};
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
test("basic functionality", () => {
|
||||
let re = /foo/;
|
||||
expect(re.exec.length).toBe(1);
|
||||
|
||||
let res = re.exec("foo");
|
||||
expect(res.length).toBe(1);
|
||||
expect(res[0]).toBe("foo");
|
||||
expect(res.groups).toBe(undefined);
|
||||
expect(res.index).toBe(0);
|
||||
});
|
||||
|
||||
test("basic unnamed captures", () => {
|
||||
let re = /f(o.*)/;
|
||||
let res = re.exec("fooooo");
|
||||
|
||||
expect(res.length).toBe(2);
|
||||
expect(res[0]).toBe("fooooo");
|
||||
expect(res[1]).toBe("ooooo");
|
||||
expect(res.groups).toBe(undefined);
|
||||
expect(res.index).toBe(0);
|
||||
});
|
||||
|
||||
test("basic named captures", () => {
|
||||
let re = /f(?<os>o.*)/;
|
||||
let res = re.exec("fooooo");
|
||||
|
||||
expect(res.length).toBe(1);
|
||||
expect(res.index).toBe(0);
|
||||
expect(res[0]).toBe("fooooo");
|
||||
expect(res.groups).not.toBe(undefined);
|
||||
expect(res.groups.os).toBe("ooooo");
|
||||
});
|
||||
|
||||
test("basic index", () => {
|
||||
let re = /foo/;
|
||||
let res = re.exec("abcfoo");
|
||||
|
||||
expect(res.length).toBe(1);
|
||||
expect(res.index).toBe(3);
|
||||
expect(res[0]).toBe("foo");
|
||||
});
|
||||
|
||||
test("basic index with global and initial offset", () => {
|
||||
let re = /foo/g;
|
||||
re.lastIndex = 2;
|
||||
let res = re.exec("abcfoo");
|
||||
|
||||
expect(res.length).toBe(1);
|
||||
expect(res.index).toBe(3);
|
||||
expect(res[0]).toBe("foo");
|
||||
});
|
||||
|
||||
test("not matching", () => {
|
||||
let re = /foo/;
|
||||
let res = re.exec("bar");
|
||||
|
||||
expect(res).toBe(null);
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue