diff --git a/README.md b/README.md
index 864f76e..c7da631 100644
--- a/README.md
+++ b/README.md
@@ -553,6 +553,7 @@ Notice that nearly every feature below has at least subtle differences from Java
✅ |
✔ Unescaped - outside of range is literal in some contexts (different than JS rules in any mode)
+ ✔ Error for unescaped [ that doesn't form nested class
✔ Fewer chars require escaping than JS
|
diff --git a/spec/match-group.spec.js b/spec/match-group.spec.js
index 1f7d24f..099fa17 100644
--- a/spec/match-group.spec.js
+++ b/spec/match-group.spec.js
@@ -5,23 +5,65 @@ beforeEach(() => {
jasmine.addMatchers(matchers);
});
-// TODO: Add me
-// describe('Group', () => {
-// describe('atomic', () => {
-// it('should', () => {
-// expect('').toExactlyMatch(r``);
-// });
-// });
-
-// describe('flags', () => {
-// it('should', () => {
-// expect('').toExactlyMatch(r``);
-// });
-// });
-
-// describe('noncapturing', () => {
-// it('should', () => {
-// expect('').toExactlyMatch(r``);
-// });
-// });
-// });
+describe('Group', () => {
+ describe('atomic', () => {
+ it('should not remember backtracking positions within atomic groups', () => {
+ expect('abc').not.toFindMatch(`a(?>bc|b)c`);
+ expect('abcc').toExactlyMatch(`a(?>bc|b)c`);
+ expect('aaaaaab').not.toFindMatch(`(?>a+)ab`);
+ expect('aaaaaab').toExactlyMatch(`(?>a)+ab`);
+ });
+
+ it('should allow quantifying atomic groups', () => {
+ expect('one two').toExactlyMatch(r`(?>\w+\s?)+`);
+ });
+
+ it('should work for multiple atomic groups', () => {
+ expect('ab').toExactlyMatch(`(?>a)(?>b)`);
+ });
+
+ it('should work for nested atomic groups', () => {
+ expect('integerrr+').toExactlyMatch(r`\b(?>int(?>eger+)?|insert)\b(?>.)`);
+ expect('integerrr+').not.toFindMatch(r`\b(?>int(?>eger+)??|insert)\b(?>.)`);
+ });
+
+ it('should work when named capturing groups present', () => {
+ expect('abcc').toExactlyMatch(`(?)a(?>bc|b)c`);
+ expect('abc').not.toFindMatch(`(?)a(?>bc|b)c`);
+ expect('abcc').toExactlyMatch(`a(?>(?)bc|b)c`);
+ expect('abc').not.toFindMatch(`a(?>(?)bc|b)c`);
+ });
+
+ it('should work when unnamed capturing groups present', () => {
+ expect('abcc').toExactlyMatch(`()a(?>bc|b)c`);
+ expect('abc').not.toFindMatch(`()a(?>bc|b)c`);
+ expect('abcc').toExactlyMatch(`a(?>()bc|b)c`);
+ expect('abc').not.toFindMatch(`a(?>()bc|b)c`);
+ });
+
+ it('should work with numbered backreferences', () => {
+ expect('aax').toExactlyMatch(r`(a)\1(?>x)`);
+ expect('xaa').toExactlyMatch(r`(?>x(a)\1)`);
+ expect('xaa').toExactlyMatch(r`(?>x)(a)\1`);
+ expect('aaabababcabc').toExactlyMatch(r`(a)\1(?>\1(b)\1\2(?>\1\2))(c)\1\2\3`);
+ });
+
+ it('should handle regression cases', () => {
+ expect('[').toExactlyMatch(r`(?>[\[])`);
+ });
+ });
+
+ // TODO: Add me
+ // describe('flags', () => {
+ // it('should', () => {
+ // expect('').toExactlyMatch(r``);
+ // });
+ // });
+
+ // TODO: Add me
+ // describe('noncapturing', () => {
+ // it('should', () => {
+ // expect('').toExactlyMatch(r``);
+ // });
+ // });
+});
diff --git a/src/generate.js b/src/generate.js
index c9890ef..5707306 100644
--- a/src/generate.js
+++ b/src/generate.js
@@ -180,6 +180,9 @@ const BaseEscapeChars = new Set([
]);
const CharClassEscapeChars = new Set([
'-', '\\', ']', '^',
+ // Literal `[` doesn't require escaping with flag u, but this can help work around regex source
+ // linters and regex syntax processors that expect unescaped `[` to create a nested class
+ '[',
]);
const CharClassEscapeCharsFlagV = new Set([
'(', ')', '-', '/', '[', '\\', ']', '^', '{', '|', '}',