Skip to content

Commit

Permalink
Support \G in leading positive lookaround
Browse files Browse the repository at this point in the history
  • Loading branch information
slevithan committed Nov 3, 2024
1 parent 3c44de2 commit 4417e11
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 3 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -578,7 +578,7 @@ Notice that nearly every feature below has at least subtle differences from Java
<td align="middle">☑️</td>
<td align="middle">☑️</td>
<td>
Supported when used at start of pattern (if no top-level alternation) and when at start of all top-level alternatives<br>
Many common uses supported<br>
</td>
</tr>
<tr valign="top">
Expand Down
18 changes: 17 additions & 1 deletion spec/match-assertion.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ describe('Assertion', () => {
expect('a').toExactlyMatch(r`\A\Ga`);
expect('a').toExactlyMatch(r`\b\Ga`);
expect('a').toExactlyMatch(r`(?=a)\Ga`);
expect('a').toExactlyMatch(r`(?<=\A)\Ga`);
expect('a').toExactlyMatch(r`(?<!a)\Ga`);
expect('a').toExactlyMatch(r`(?<!a)(?=a)\Ga`);
});
Expand Down Expand Up @@ -120,8 +121,23 @@ describe('Assertion', () => {
expect(() => compile(r`(\Ga)+\G`)).toThrow();
});

it('should allow if leading in a leading positive lookaround', () => {
expect('a').toExactlyMatch(r`(?=\G)a`);
expect('a').toExactlyMatch(r`(?<=\G)a`);
expect(() => compile(r`(?<=a\G)a`)).toThrow();
expect(() => compile(r`(?<=\G|)a`)).toThrow();
expect(() => compile(r`(?:(?<=\G))?a`)).toThrow();
expect('a').toExactlyMatch(r`(?=\G)a|\Gb`);
expect(() => compile(r`(?=\G)a|b`)).toThrow();
});

it('should throw if leading in a leading negative lookaround', () => {
expect(() => compile(r`(?!\G)a`)).toThrow();
expect(() => compile(r`(?<!\G)a`)).toThrow();
});

// Documenting current behavior; supportable
it('should allow redundant assertions', () => {
it('should throw for redundant assertions', () => {
expect(() => compile(r`\G\Ga`)).toThrow();
expect(() => compile(r`\Ga|\G\Gb`)).toThrow();
});
Expand Down
14 changes: 13 additions & 1 deletion src/transform.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import emojiRegex from 'emoji-regex-xs';
import {AstAssertionKinds, AstCharacterSetKinds, AstDirectiveKinds, AstTypes, AstVariableLengthCharacterSetKinds, createAlternative, createBackreference, createGroup, createLookaround, createUnicodeProperty, parse} from './parse.js';
import {AstAssertionKinds, AstCharacterSetKinds, AstDirectiveKinds, AstTypes, AstVariableLengthCharacterSetKinds, createAlternative, createBackreference, createGroup, createLookaround, createUnicodeProperty, isLookaround, parse} from './parse.js';
import {tokenize} from './tokenize.js';
import {traverse} from './traverse.js';
import {JsUnicodeProperties, PosixClassesMap} from './unicode.js';
Expand Down Expand Up @@ -645,6 +645,18 @@ function hasLeadingG(els, supportedGNodes) {
if (!els.length) {
return false;
}
const first = els[0];
// Special case for leading positive lookaround with leading `\G`, else all leading assertions
// are ignored when looking for `\G`
if (
isLookaround(first) &&
!first.negate &&
first.alternatives.length === 1 &&
first.alternatives[0].elements[0]?.kind === AstAssertionKinds.search_start
) {
supportedGNodes.add(first.alternatives[0].elements[0]);
return true;
}
const firstToConsider = els.find(el => {
return el.kind === AstAssertionKinds.search_start ?
true :
Expand Down

0 comments on commit 4417e11

Please sign in to comment.