Repository navigation
Closed
Description
Summary
Perry's RegExp runtime records and implements only g, i, and m in the object header. Node exposes additional observable flags and matching semantics for y (sticky), s (dotAll), u (unicode), and d (hasIndices). Perry currently reports these flag properties as false and does not implement sticky matching or match indices.
Expected Node behavior
Local Node v25.9.0 probe:
const sticky = /a/y;
console.log(sticky.flags, sticky.sticky, sticky.global, sticky.lastIndex); // "y", true, false, 0
console.log(sticky.exec("ba"), sticky.lastIndex); // null, 0
sticky.lastIndex = 1;
console.log(sticky.exec("ba").index, sticky.lastIndex); // 1, 2
const dotAll = /a.b/s;
console.log(dotAll.flags, dotAll.dotAll, dotAll.test("a\nb")); // "s", true, true
const unicode = /./u;
console.log(unicode.flags, unicode.unicode); // "u", true
const indexed = /(a)/d;
const match = indexed.exec("a");
console.log(indexed.flags, indexed.hasIndices, match.indices[0], match.indices[1]);
// "d", true, [0, 1], [0, 1]Current Perry implementation
Source evidence from current tree:
crates/perry-runtime/src/regex.rs::RegExpHeaderstorescase_insensitive,global,multiline, andlast_index, but has no sticky/dotAll/unicode/hasIndices booleans.js_regexp_new()derives onlyflags_str.contains('i'),contains('g'), andcontains('m'); other flags remain only in the saved flags string.get_or_compile_regex()only injects Rust regex options for case-insensitive and multiline modes. There is no dotAll translation (s) such as(?s)/ equivalent, so.will not match newlines for/spatterns.js_regexp_exec()treats onlyglobalas stateful: it starts atlast_indexonly whenglobalis true and resets/updateslast_indexonly on that path. Sticky regexes should also be stateful and must match exactly atlastIndexinstead of searching later in the string.crates/perry-runtime/src/object/field_get_set.rsroutes RegExp property reads for.source,.flags,.lastIndex,.global,.ignoreCase, and.multiline, but returnsfalsefor.sticky,.unicode,.dotAll, and.hasIndicesunconditionally.- There is no visible support for the
dflag'smatch.indicesarray in thejs_regexp_exec()result path.
Suggested test surface
Add parity tests for the properties and stateful matching:
const sticky = /a/y;
assert.equal(sticky.flags, "y");
assert.equal(sticky.sticky, true);
assert.equal(sticky.global, false);
assert.equal(sticky.exec("ba"), null);
sticky.lastIndex = 1;
const sm = sticky.exec("ba")!;
assert.equal(sm.index, 1);
assert.equal(sticky.lastIndex, 2);
const dotAll = /a.b/s;
assert.equal(dotAll.dotAll, true);
assert.equal(dotAll.test("a\nb"), true);
const unicode = /./u;
assert.equal(unicode.unicode, true);
const indexed = /(a)/d;
const im = indexed.exec("a")! as any;
assert.equal(indexed.hasIndices, true);
assert.deepEqual(im.indices[0], [0, 1]);
assert.deepEqual(im.indices[1], [0, 1]);Scope / non-goals
This is separate from earlier RegExp allocation/router fixes such as #637. It is specifically about RegExp flag property values and the matching/result semantics those flags enable.
Metadata
Metadata
Assignees
Labels
No labels
Activity
fix(regexp): flag/pattern validation and flag property getters (#3480)