Skip to content

Commit

Permalink
Don't rely on lookbehind in subclass
Browse files Browse the repository at this point in the history
  • Loading branch information
slevithan committed Nov 15, 2024
1 parent c1a1e1f commit 0488c55
Show file tree
Hide file tree
Showing 5 changed files with 14 additions and 10 deletions.
3 changes: 2 additions & 1 deletion src/atomic.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {emulationGroupMarker, noncapturingDelim, spliceStr} from './utils.js';
import {emulationGroupMarker} from './subclass.js';
import {noncapturingDelim, spliceStr} from './utils.js';
import {Context, replaceUnescaped} from 'regex-utilities';

const atomicPluginToken = new RegExp(String.raw`(?<noncapturingStart>${noncapturingDelim})|(?<capturingStart>\((?:\?<[^>]+>)?)|\\?.`, 'gsu');
Expand Down
3 changes: 2 additions & 1 deletion src/flag-x.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {CharClassContext, doublePunctuatorChars, emulationGroupMarker, getEndContextForIncompleteExpression, noncapturingDelim, RegexContext, sandboxLoneDoublePunctuatorChar, sandboxUnsafeNulls} from './utils.js';
import {emulationGroupMarker} from './subclass.js';
import {CharClassContext, doublePunctuatorChars, getEndContextForIncompleteExpression, noncapturingDelim, RegexContext, sandboxLoneDoublePunctuatorChar, sandboxUnsafeNulls} from './utils.js';
import {Context, replaceUnescaped} from 'regex-utilities';

const ws = /^\s$/;
Expand Down
11 changes: 8 additions & 3 deletions src/subclass.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
import {capturingDelim, emulationGroupMarker} from './utils.js';
import {Context, replaceUnescaped} from 'regex-utilities';

// This marker was chosen because it's impossible to match (so its extemely unlikely to be used in
// a user-provided regex); it's not at risk of being optimized away, transformed, or flagged as an
// error by a plugin; and it ends with an unquantifiable token
const emulationGroupMarker = '$E$';

/**
@class
@param {string | RegExpSubclass} expression
@param {string} [flags]
@param {{useEmulationGroups: boolean;}} [options]
*/
class RegExpSubclass extends RegExp {
// Avoid #private to allow for subclassing
_captureMap;
constructor(expression, flags, options) {
let captureMap;
Expand All @@ -20,7 +25,6 @@ class RegExpSubclass extends RegExp {
// The third argument `options` isn't provided when regexes are copied as part of the internal
// handling of string methods `matchAll` and `split`
} else if (expression instanceof RegExpSubclass) {
// Can read private properties of the existing object since it was created by this class
this._captureMap = expression._captureMap;
}
}
Expand Down Expand Up @@ -67,7 +71,7 @@ function unmarkEmulationGroups(expression) {
const captureMap = [true];
expression = replaceUnescaped(
expression,
`(?:${capturingDelim})(?<mark>${marker})?`,
String.raw`\((?:(?!\?)|\?<(?![=!])[^>]+>)(?<mark>${marker})?`,
({0: m, groups: {mark}}) => {
if (mark) {
captureMap.push(false);
Expand All @@ -85,5 +89,6 @@ function unmarkEmulationGroups(expression) {
}

export {
emulationGroupMarker,
RegExpSubclass,
};
3 changes: 2 additions & 1 deletion src/subroutines.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {capturingDelim, countCaptures, emulationGroupMarker, namedCapturingDelim, spliceStr} from './utils.js';
import {emulationGroupMarker} from './subclass.js';
import {capturingDelim, countCaptures, namedCapturingDelim, spliceStr} from './utils.js';
import {Context, execUnescaped, forEachUnescaped, getGroupContents, hasUnescaped, replaceUnescaped} from 'regex-utilities';

/**
Expand Down
4 changes: 0 additions & 4 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,6 @@ export const flagVSupported = (() => {
return true;
})();

// This marker was chosen because it's impossible to match (so its extemely unlikely to be used in
// a user-provided regex); it's not at risk of being optimized away, transformed, or flagged as an
// error by a plugin; and it ends with an unquantifiable token
export const emulationGroupMarker = '$E$';
export const doublePunctuatorChars = '&!#$%*+,.:;<=>?@^`~';
export const namedCapturingDelim = String.raw`\(\?<(?![=!])(?<captureName>[^>]+)>`;
export const capturingDelim = String.raw`\((?!\?)(?!(?<=\(\?\()DEFINE\))|${namedCapturingDelim}`;
Expand Down

0 comments on commit 0488c55

Please sign in to comment.