From d1b30e0d8b623bb13bab21e35b0aa971414ed919 Mon Sep 17 00:00:00 2001 From: ota-meshi Date: Sun, 14 Apr 2024 02:14:27 +0900 Subject: [PATCH] feat: add support for ES2025 duplicate named capturing groups --- src/ecma-versions.ts | 3 +- src/group-specifiers.ts | 104 + src/parser.ts | 3 +- src/validator.ts | 26 +- ...te-named-capturing-group-invalid-2024.json | 14 + ...te-named-capturing-group-invalid-2025.json | 14 + ...cate-named-capturing-group-valid-2025.json | 291 +++ ...named-groups-and-regexp-match-indices.json | 524 ++++- .../regexp-duplicate-named-groups.json | 1718 ++++++++++++++++- 9 files changed, 2615 insertions(+), 82 deletions(-) create mode 100644 src/group-specifiers.ts create mode 100644 test/fixtures/parser/literal/duplicate-named-capturing-group-invalid-2024.json create mode 100644 test/fixtures/parser/literal/duplicate-named-capturing-group-invalid-2025.json create mode 100644 test/fixtures/parser/literal/duplicate-named-capturing-group-valid-2025.json diff --git a/src/ecma-versions.ts b/src/ecma-versions.ts index 4ce2cb8..3a6b1d9 100644 --- a/src/ecma-versions.ts +++ b/src/ecma-versions.ts @@ -10,4 +10,5 @@ export type EcmaVersion = | 2022 | 2023 | 2024 -export const latestEcmaVersion = 2024 + | 2025 +export const latestEcmaVersion = 2025 diff --git a/src/group-specifiers.ts b/src/group-specifiers.ts new file mode 100644 index 0000000..1fcdba5 --- /dev/null +++ b/src/group-specifiers.ts @@ -0,0 +1,104 @@ +/** + * Holds information for all GroupSpecifiers included in the pattern. + */ +export interface GroupSpecifiers { + /** + * @returns true if there are no GroupSpecifiers included in the pattern. + */ + isEmpty: () => boolean + clear: () => void + /** + * Called when visiting the Alternative. + * For ES2025, manage nesting with new Alternative scopes. + */ + enterAlternative: () => void + /** + * Called when leaving the Alternative. + */ + leaveAlternative: () => void + /** + * Checks whether the given group name is within the pattern. + */ + hasInPattern: (name: string) => boolean + /** + * Checks whether the given group name is within the current scope. + */ + hasInScope: (name: string) => boolean + /** + * Adds the given group name to the current scope. + */ + addToScope: (name: string) => void +} + +export class GroupSpecifiersAsES2018 implements GroupSpecifiers { + private groupName = new Set() + + public clear(): void { + this.groupName.clear() + } + + public isEmpty(): boolean { + return !this.groupName.size + } + + public hasInPattern(name: string): boolean { + return this.groupName.has(name) + } + + public hasInScope(name: string): boolean { + return this.hasInPattern(name) + } + + public addToScope(name: string): void { + this.groupName.add(name) + } + + // eslint-disable-next-line class-methods-use-this + public enterAlternative(): void { + // Prior to ES2025, it does not manage alternative scopes. + } + + // eslint-disable-next-line class-methods-use-this + public leaveAlternative(): void { + // Prior to ES2025, it does not manage alternative scopes. + } +} + +export class GroupSpecifiersAsES2025 implements GroupSpecifiers { + private groupNamesInAlternative = new Set() + private upperGroupNamesStack: Set[] = [] + + private groupNamesInPattern = new Set() + + public clear(): void { + this.groupNamesInAlternative.clear() + this.upperGroupNamesStack.length = 0 + this.groupNamesInPattern.clear() + } + + public isEmpty(): boolean { + return !this.groupNamesInPattern.size + } + + public enterAlternative(): void { + this.upperGroupNamesStack.push(this.groupNamesInAlternative) + this.groupNamesInAlternative = new Set(this.groupNamesInAlternative) + } + + public leaveAlternative(): void { + this.groupNamesInAlternative = this.upperGroupNamesStack.pop()! + } + + public hasInPattern(name: string): boolean { + return this.groupNamesInPattern.has(name) + } + + public hasInScope(name: string): boolean { + return this.groupNamesInAlternative.has(name) + } + + public addToScope(name: string): void { + this.groupNamesInAlternative.add(name) + this.groupNamesInPattern.add(name) + } +} diff --git a/src/parser.ts b/src/parser.ts index 6ba9fbc..f5ac596 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -747,7 +747,7 @@ export namespace RegExpParser { strict?: boolean /** - * ECMAScript version. Default is `2024`. + * ECMAScript version. Default is `2025`. * - `2015` added `u` and `y` flags. * - `2018` added `s` flag, Named Capturing Group, Lookbehind Assertion, * and Unicode Property Escape. @@ -755,6 +755,7 @@ export namespace RegExpParser { * - `2022` added `d` flag. * - `2023` added more valid Unicode Property Escapes. * - `2024` added `v` flag. + * - `2025` added duplicate named capturing groups. */ ecmaVersion?: EcmaVersion } diff --git a/src/validator.ts b/src/validator.ts index 41901c9..8c2491e 100644 --- a/src/validator.ts +++ b/src/validator.ts @@ -1,5 +1,10 @@ import type { EcmaVersion } from "./ecma-versions" import { latestEcmaVersion } from "./ecma-versions" +import type { GroupSpecifiers } from "./group-specifiers" +import { + GroupSpecifiersAsES2018, + GroupSpecifiersAsES2025, +} from "./group-specifiers" import { Reader } from "./reader" import { newRegExpSyntaxError } from "./regexp-syntax-error" import { @@ -231,7 +236,7 @@ export namespace RegExpValidator { strict?: boolean /** - * ECMAScript version. Default is `2024`. + * ECMAScript version. Default is `2025`. * - `2015` added `u` and `y` flags. * - `2018` added `s` flag, Named Capturing Group, Lookbehind Assertion, * and Unicode Property Escape. @@ -239,6 +244,7 @@ export namespace RegExpValidator { * - `2022` added `d` flag. * - `2023` added more valid Unicode Property Escapes. * - `2024` added `v` flag. + * - `2025` added duplicate named capturing groups. */ ecmaVersion?: EcmaVersion @@ -631,7 +637,7 @@ export class RegExpValidator { private _numCapturingParens = 0 - private _groupNames = new Set() + private _groupSpecifiers: GroupSpecifiers private _backreferenceNames = new Set() @@ -643,6 +649,10 @@ export class RegExpValidator { */ public constructor(options?: RegExpValidator.Options) { this._options = options ?? {} + this._groupSpecifiers = + this.ecmaVersion >= 2025 + ? new GroupSpecifiersAsES2025() + : new GroupSpecifiersAsES2018() } /** @@ -763,7 +773,7 @@ export class RegExpValidator { if ( !this._nFlag && this.ecmaVersion >= 2018 && - this._groupNames.size > 0 + !this._groupSpecifiers.isEmpty() ) { this._nFlag = true this.rewind(start) @@ -1301,7 +1311,7 @@ export class RegExpValidator { private consumePattern(): void { const start = this.index this._numCapturingParens = this.countCapturingParens() - this._groupNames.clear() + this._groupSpecifiers.clear() this._backreferenceNames.clear() this.onPatternEnter(start) @@ -1322,7 +1332,7 @@ export class RegExpValidator { this.raise(`Unexpected character '${c}'`) } for (const name of this._backreferenceNames) { - if (!this._groupNames.has(name)) { + if (!this._groupSpecifiers.hasInPattern(name)) { this.raise("Invalid named capture referenced") } } @@ -1380,7 +1390,9 @@ export class RegExpValidator { this.onDisjunctionEnter(start) do { + this._groupSpecifiers.enterAlternative() this.consumeAlternative(i++) + this._groupSpecifiers.leaveAlternative() } while (this.eat(VERTICAL_LINE)) if (this.consumeQuantifier(true)) { @@ -1846,8 +1858,8 @@ export class RegExpValidator { private consumeGroupSpecifier(): boolean { if (this.eat(QUESTION_MARK)) { if (this.eatGroupName()) { - if (!this._groupNames.has(this._lastStrValue)) { - this._groupNames.add(this._lastStrValue) + if (!this._groupSpecifiers.hasInScope(this._lastStrValue)) { + this._groupSpecifiers.addToScope(this._lastStrValue) return true } this.raise("Duplicate capture group name") diff --git a/test/fixtures/parser/literal/duplicate-named-capturing-group-invalid-2024.json b/test/fixtures/parser/literal/duplicate-named-capturing-group-invalid-2024.json new file mode 100644 index 0000000..e61353f --- /dev/null +++ b/test/fixtures/parser/literal/duplicate-named-capturing-group-invalid-2024.json @@ -0,0 +1,14 @@ +{ + "options": { + "strict": false, + "ecmaVersion": 2024 + }, + "patterns": { + "/(?[0-9]{4})-[0-9]{2}|[0-9]{2}-(?[0-9]{4})/": { + "error": { + "message": "Invalid regular expression: /(?[0-9]{4})-[0-9]{2}|[0-9]{2}-(?[0-9]{4})/: Duplicate capture group name", + "index": 45 + } + } + } +} \ No newline at end of file diff --git a/test/fixtures/parser/literal/duplicate-named-capturing-group-invalid-2025.json b/test/fixtures/parser/literal/duplicate-named-capturing-group-invalid-2025.json new file mode 100644 index 0000000..670262f --- /dev/null +++ b/test/fixtures/parser/literal/duplicate-named-capturing-group-invalid-2025.json @@ -0,0 +1,14 @@ +{ + "options": { + "strict": false, + "ecmaVersion": 2025 + }, + "patterns": { + "/(?[0-9]{4})-(?[0-9]{2})/": { + "error": { + "message": "Invalid regular expression: /(?[0-9]{4})-(?[0-9]{2})/: Duplicate capture group name", + "index": 27 + } + } + } +} \ No newline at end of file diff --git a/test/fixtures/parser/literal/duplicate-named-capturing-group-valid-2025.json b/test/fixtures/parser/literal/duplicate-named-capturing-group-valid-2025.json new file mode 100644 index 0000000..63ee83a --- /dev/null +++ b/test/fixtures/parser/literal/duplicate-named-capturing-group-valid-2025.json @@ -0,0 +1,291 @@ +{ + "options": { + "strict": false, + "ecmaVersion": 2025 + }, + "patterns": { + "/(?[0-9]{4})-[0-9]{2}|[0-9]{2}-(?[0-9]{4})/": { + "ast": { + "type": "RegExpLiteral", + "parent": null, + "start": 0, + "end": 55, + "raw": "/(?[0-9]{4})-[0-9]{2}|[0-9]{2}-(?[0-9]{4})/", + "pattern": { + "type": "Pattern", + "parent": "♻️..", + "start": 1, + "end": 54, + "raw": "(?[0-9]{4})-[0-9]{2}|[0-9]{2}-(?[0-9]{4})", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 1, + "end": 27, + "raw": "(?[0-9]{4})-[0-9]{2}", + "elements": [ + { + "type": "CapturingGroup", + "parent": "♻️../..", + "start": 1, + "end": 18, + "raw": "(?[0-9]{4})", + "name": "year", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 9, + "end": 17, + "raw": "[0-9]{4}", + "elements": [ + { + "type": "Quantifier", + "parent": "♻️../..", + "start": 9, + "end": 17, + "raw": "[0-9]{4}", + "min": 4, + "max": 4, + "greedy": true, + "element": { + "type": "CharacterClass", + "parent": "♻️..", + "start": 9, + "end": 14, + "raw": "[0-9]", + "unicodeSets": false, + "negate": false, + "elements": [ + { + "type": "CharacterClassRange", + "parent": "♻️../..", + "start": 10, + "end": 13, + "raw": "0-9", + "min": { + "type": "Character", + "parent": "♻️..", + "start": 10, + "end": 11, + "raw": "0", + "value": 48 + }, + "max": { + "type": "Character", + "parent": "♻️..", + "start": 12, + "end": 13, + "raw": "9", + "value": 57 + } + } + ] + } + } + ] + } + ], + "references": [] + }, + { + "type": "Character", + "parent": "♻️../..", + "start": 18, + "end": 19, + "raw": "-", + "value": 45 + }, + { + "type": "Quantifier", + "parent": "♻️../..", + "start": 19, + "end": 27, + "raw": "[0-9]{2}", + "min": 2, + "max": 2, + "greedy": true, + "element": { + "type": "CharacterClass", + "parent": "♻️..", + "start": 19, + "end": 24, + "raw": "[0-9]", + "unicodeSets": false, + "negate": false, + "elements": [ + { + "type": "CharacterClassRange", + "parent": "♻️../..", + "start": 20, + "end": 23, + "raw": "0-9", + "min": { + "type": "Character", + "parent": "♻️..", + "start": 20, + "end": 21, + "raw": "0", + "value": 48 + }, + "max": { + "type": "Character", + "parent": "♻️..", + "start": 22, + "end": 23, + "raw": "9", + "value": 57 + } + } + ] + } + } + ] + }, + { + "type": "Alternative", + "parent": "♻️../..", + "start": 28, + "end": 54, + "raw": "[0-9]{2}-(?[0-9]{4})", + "elements": [ + { + "type": "Quantifier", + "parent": "♻️../..", + "start": 28, + "end": 36, + "raw": "[0-9]{2}", + "min": 2, + "max": 2, + "greedy": true, + "element": { + "type": "CharacterClass", + "parent": "♻️..", + "start": 28, + "end": 33, + "raw": "[0-9]", + "unicodeSets": false, + "negate": false, + "elements": [ + { + "type": "CharacterClassRange", + "parent": "♻️../..", + "start": 29, + "end": 32, + "raw": "0-9", + "min": { + "type": "Character", + "parent": "♻️..", + "start": 29, + "end": 30, + "raw": "0", + "value": 48 + }, + "max": { + "type": "Character", + "parent": "♻️..", + "start": 31, + "end": 32, + "raw": "9", + "value": 57 + } + } + ] + } + }, + { + "type": "Character", + "parent": "♻️../..", + "start": 36, + "end": 37, + "raw": "-", + "value": 45 + }, + { + "type": "CapturingGroup", + "parent": "♻️../..", + "start": 37, + "end": 54, + "raw": "(?[0-9]{4})", + "name": "year", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 45, + "end": 53, + "raw": "[0-9]{4}", + "elements": [ + { + "type": "Quantifier", + "parent": "♻️../..", + "start": 45, + "end": 53, + "raw": "[0-9]{4}", + "min": 4, + "max": 4, + "greedy": true, + "element": { + "type": "CharacterClass", + "parent": "♻️..", + "start": 45, + "end": 50, + "raw": "[0-9]", + "unicodeSets": false, + "negate": false, + "elements": [ + { + "type": "CharacterClassRange", + "parent": "♻️../..", + "start": 46, + "end": 49, + "raw": "0-9", + "min": { + "type": "Character", + "parent": "♻️..", + "start": 46, + "end": 47, + "raw": "0", + "value": 48 + }, + "max": { + "type": "Character", + "parent": "♻️..", + "start": 48, + "end": 49, + "raw": "9", + "value": 57 + } + } + ] + } + } + ] + } + ], + "references": [] + } + ] + } + ] + }, + "flags": { + "type": "Flags", + "parent": "♻️..", + "start": 55, + "end": 55, + "raw": "", + "global": false, + "ignoreCase": false, + "multiline": false, + "unicode": false, + "sticky": false, + "dotAll": false, + "hasIndices": false, + "unicodeSets": false + } + } + } + } +} \ No newline at end of file diff --git a/test/fixtures/parser/literal/test262/regexp-duplicate-named-groups-and-regexp-match-indices.json b/test/fixtures/parser/literal/test262/regexp-duplicate-named-groups-and-regexp-match-indices.json index 81e9e1d..79aafba 100644 --- a/test/fixtures/parser/literal/test262/regexp-duplicate-named-groups-and-regexp-match-indices.json +++ b/test/fixtures/parser/literal/test262/regexp-duplicate-named-groups-and-regexp-match-indices.json @@ -6,21 +6,527 @@ "options": {}, "patterns": { "/(?:(?:(?a)|(?b)|c)\\k){2}/d": { - "error": { - "message": "Invalid regular expression: /(?:(?:(?a)|(?b)|c)\\k){2}/d: Duplicate capture group name", - "index": 20 + "ast": { + "type": "RegExpLiteral", + "parent": null, + "start": 0, + "end": 36, + "raw": "/(?:(?:(?a)|(?b)|c)\\k){2}/d", + "pattern": { + "type": "Pattern", + "parent": "♻️..", + "start": 1, + "end": 34, + "raw": "(?:(?:(?a)|(?b)|c)\\k){2}", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 1, + "end": 34, + "raw": "(?:(?:(?a)|(?b)|c)\\k){2}", + "elements": [ + { + "type": "Quantifier", + "parent": "♻️../..", + "start": 1, + "end": 34, + "raw": "(?:(?:(?a)|(?b)|c)\\k){2}", + "min": 2, + "max": 2, + "greedy": true, + "element": { + "type": "Group", + "parent": "♻️..", + "start": 1, + "end": 31, + "raw": "(?:(?:(?a)|(?b)|c)\\k)", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 4, + "end": 30, + "raw": "(?:(?a)|(?b)|c)\\k", + "elements": [ + { + "type": "Group", + "parent": "♻️../..", + "start": 4, + "end": 25, + "raw": "(?:(?a)|(?b)|c)", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 7, + "end": 14, + "raw": "(?a)", + "elements": [ + { + "type": "CapturingGroup", + "parent": "♻️../..", + "start": 7, + "end": 14, + "raw": "(?a)", + "name": "x", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 12, + "end": 13, + "raw": "a", + "elements": [ + { + "type": "Character", + "parent": "♻️../..", + "start": 12, + "end": 13, + "raw": "a", + "value": 97 + } + ] + } + ], + "references": [ + "♻️../../../../../1" + ] + } + ] + }, + { + "type": "Alternative", + "parent": "♻️../..", + "start": 15, + "end": 22, + "raw": "(?b)", + "elements": [ + { + "type": "CapturingGroup", + "parent": "♻️../..", + "start": 15, + "end": 22, + "raw": "(?b)", + "name": "x", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 20, + "end": 21, + "raw": "b", + "elements": [ + { + "type": "Character", + "parent": "♻️../..", + "start": 20, + "end": 21, + "raw": "b", + "value": 98 + } + ] + } + ], + "references": [] + } + ] + }, + { + "type": "Alternative", + "parent": "♻️../..", + "start": 23, + "end": 24, + "raw": "c", + "elements": [ + { + "type": "Character", + "parent": "♻️../..", + "start": 23, + "end": 24, + "raw": "c", + "value": 99 + } + ] + } + ] + }, + { + "type": "Backreference", + "parent": "♻️../..", + "start": 25, + "end": 30, + "raw": "\\k", + "ref": "x", + "resolved": "♻️../0/alternatives/0/elements/0" + } + ] + } + ] + } + } + ] + } + ] + }, + "flags": { + "type": "Flags", + "parent": "♻️..", + "start": 35, + "end": 36, + "raw": "d", + "global": false, + "ignoreCase": false, + "multiline": false, + "unicode": false, + "sticky": false, + "dotAll": false, + "hasIndices": true, + "unicodeSets": false + } } }, "/(?:(?a)|(?a)(?b))(?:(?c)|(?d))/d": { - "error": { - "message": "Invalid regular expression: /(?:(?a)|(?a)(?b))(?:(?c)|(?d))/d: Duplicate capture group name", - "index": 24 + "ast": { + "type": "RegExpLiteral", + "parent": null, + "start": 0, + "end": 48, + "raw": "/(?:(?a)|(?a)(?b))(?:(?c)|(?d))/d", + "pattern": { + "type": "Pattern", + "parent": "♻️..", + "start": 1, + "end": 46, + "raw": "(?:(?a)|(?a)(?b))(?:(?c)|(?d))", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 1, + "end": 46, + "raw": "(?:(?a)|(?a)(?b))(?:(?c)|(?d))", + "elements": [ + { + "type": "Group", + "parent": "♻️../..", + "start": 1, + "end": 27, + "raw": "(?:(?a)|(?a)(?b))", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 4, + "end": 11, + "raw": "(?a)", + "elements": [ + { + "type": "CapturingGroup", + "parent": "♻️../..", + "start": 4, + "end": 11, + "raw": "(?a)", + "name": "x", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 9, + "end": 10, + "raw": "a", + "elements": [ + { + "type": "Character", + "parent": "♻️../..", + "start": 9, + "end": 10, + "raw": "a", + "value": 97 + } + ] + } + ], + "references": [] + } + ] + }, + { + "type": "Alternative", + "parent": "♻️../..", + "start": 12, + "end": 26, + "raw": "(?a)(?b)", + "elements": [ + { + "type": "CapturingGroup", + "parent": "♻️../..", + "start": 12, + "end": 19, + "raw": "(?a)", + "name": "y", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 17, + "end": 18, + "raw": "a", + "elements": [ + { + "type": "Character", + "parent": "♻️../..", + "start": 17, + "end": 18, + "raw": "a", + "value": 97 + } + ] + } + ], + "references": [] + }, + { + "type": "CapturingGroup", + "parent": "♻️../..", + "start": 19, + "end": 26, + "raw": "(?b)", + "name": "x", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 24, + "end": 25, + "raw": "b", + "elements": [ + { + "type": "Character", + "parent": "♻️../..", + "start": 24, + "end": 25, + "raw": "b", + "value": 98 + } + ] + } + ], + "references": [] + } + ] + } + ] + }, + { + "type": "Group", + "parent": "♻️../..", + "start": 27, + "end": 46, + "raw": "(?:(?c)|(?d))", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 30, + "end": 37, + "raw": "(?c)", + "elements": [ + { + "type": "CapturingGroup", + "parent": "♻️../..", + "start": 30, + "end": 37, + "raw": "(?c)", + "name": "z", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 35, + "end": 36, + "raw": "c", + "elements": [ + { + "type": "Character", + "parent": "♻️../..", + "start": 35, + "end": 36, + "raw": "c", + "value": 99 + } + ] + } + ], + "references": [] + } + ] + }, + { + "type": "Alternative", + "parent": "♻️../..", + "start": 38, + "end": 45, + "raw": "(?d)", + "elements": [ + { + "type": "CapturingGroup", + "parent": "♻️../..", + "start": 38, + "end": 45, + "raw": "(?d)", + "name": "z", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 43, + "end": 44, + "raw": "d", + "elements": [ + { + "type": "Character", + "parent": "♻️../..", + "start": 43, + "end": 44, + "raw": "d", + "value": 100 + } + ] + } + ], + "references": [] + } + ] + } + ] + } + ] + } + ] + }, + "flags": { + "type": "Flags", + "parent": "♻️..", + "start": 47, + "end": 48, + "raw": "d", + "global": false, + "ignoreCase": false, + "multiline": false, + "unicode": false, + "sticky": false, + "dotAll": false, + "hasIndices": true, + "unicodeSets": false + } } }, "/(?a)|(?b)/d": { - "error": { - "message": "Invalid regular expression: /(?a)|(?b)/d: Duplicate capture group name", - "index": 14 + "ast": { + "type": "RegExpLiteral", + "parent": null, + "start": 0, + "end": 18, + "raw": "/(?a)|(?b)/d", + "pattern": { + "type": "Pattern", + "parent": "♻️..", + "start": 1, + "end": 16, + "raw": "(?a)|(?b)", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 1, + "end": 8, + "raw": "(?a)", + "elements": [ + { + "type": "CapturingGroup", + "parent": "♻️../..", + "start": 1, + "end": 8, + "raw": "(?a)", + "name": "x", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 6, + "end": 7, + "raw": "a", + "elements": [ + { + "type": "Character", + "parent": "♻️../..", + "start": 6, + "end": 7, + "raw": "a", + "value": 97 + } + ] + } + ], + "references": [] + } + ] + }, + { + "type": "Alternative", + "parent": "♻️../..", + "start": 9, + "end": 16, + "raw": "(?b)", + "elements": [ + { + "type": "CapturingGroup", + "parent": "♻️../..", + "start": 9, + "end": 16, + "raw": "(?b)", + "name": "x", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 14, + "end": 15, + "raw": "b", + "elements": [ + { + "type": "Character", + "parent": "♻️../..", + "start": 14, + "end": 15, + "raw": "b", + "value": 98 + } + ] + } + ], + "references": [] + } + ] + } + ] + }, + "flags": { + "type": "Flags", + "parent": "♻️..", + "start": 17, + "end": 18, + "raw": "d", + "global": false, + "ignoreCase": false, + "multiline": false, + "unicode": false, + "sticky": false, + "dotAll": false, + "hasIndices": true, + "unicodeSets": false + } } } } diff --git a/test/fixtures/parser/literal/test262/regexp-duplicate-named-groups.json b/test/fixtures/parser/literal/test262/regexp-duplicate-named-groups.json index ed7b5e0..0bcbe63 100644 --- a/test/fixtures/parser/literal/test262/regexp-duplicate-named-groups.json +++ b/test/fixtures/parser/literal/test262/regexp-duplicate-named-groups.json @@ -9,27 +9,720 @@ "options": {}, "patterns": { "/(?:(?:(?a)|(?b))\\k){2}/": { - "error": { - "message": "Invalid regular expression: /(?:(?:(?a)|(?b))\\k){2}/: Duplicate capture group name", - "index": 20 + "ast": { + "type": "RegExpLiteral", + "parent": null, + "start": 0, + "end": 33, + "raw": "/(?:(?:(?a)|(?b))\\k){2}/", + "pattern": { + "type": "Pattern", + "parent": "♻️..", + "start": 1, + "end": 32, + "raw": "(?:(?:(?a)|(?b))\\k){2}", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 1, + "end": 32, + "raw": "(?:(?:(?a)|(?b))\\k){2}", + "elements": [ + { + "type": "Quantifier", + "parent": "♻️../..", + "start": 1, + "end": 32, + "raw": "(?:(?:(?a)|(?b))\\k){2}", + "min": 2, + "max": 2, + "greedy": true, + "element": { + "type": "Group", + "parent": "♻️..", + "start": 1, + "end": 29, + "raw": "(?:(?:(?a)|(?b))\\k)", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 4, + "end": 28, + "raw": "(?:(?a)|(?b))\\k", + "elements": [ + { + "type": "Group", + "parent": "♻️../..", + "start": 4, + "end": 23, + "raw": "(?:(?a)|(?b))", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 7, + "end": 14, + "raw": "(?a)", + "elements": [ + { + "type": "CapturingGroup", + "parent": "♻️../..", + "start": 7, + "end": 14, + "raw": "(?a)", + "name": "x", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 12, + "end": 13, + "raw": "a", + "elements": [ + { + "type": "Character", + "parent": "♻️../..", + "start": 12, + "end": 13, + "raw": "a", + "value": 97 + } + ] + } + ], + "references": [ + "♻️../../../../../1" + ] + } + ] + }, + { + "type": "Alternative", + "parent": "♻️../..", + "start": 15, + "end": 22, + "raw": "(?b)", + "elements": [ + { + "type": "CapturingGroup", + "parent": "♻️../..", + "start": 15, + "end": 22, + "raw": "(?b)", + "name": "x", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 20, + "end": 21, + "raw": "b", + "elements": [ + { + "type": "Character", + "parent": "♻️../..", + "start": 20, + "end": 21, + "raw": "b", + "value": 98 + } + ] + } + ], + "references": [] + } + ] + } + ] + }, + { + "type": "Backreference", + "parent": "♻️../..", + "start": 23, + "end": 28, + "raw": "\\k", + "ref": "x", + "resolved": "♻️../0/alternatives/0/elements/0" + } + ] + } + ] + } + } + ] + } + ] + }, + "flags": { + "type": "Flags", + "parent": "♻️..", + "start": 33, + "end": 33, + "raw": "", + "global": false, + "ignoreCase": false, + "multiline": false, + "unicode": false, + "sticky": false, + "dotAll": false, + "hasIndices": false, + "unicodeSets": false + } } }, "/(?:(?:(?a)|(?b)|c)\\k){2}/": { - "error": { - "message": "Invalid regular expression: /(?:(?:(?a)|(?b)|c)\\k){2}/: Duplicate capture group name", - "index": 20 + "ast": { + "type": "RegExpLiteral", + "parent": null, + "start": 0, + "end": 35, + "raw": "/(?:(?:(?a)|(?b)|c)\\k){2}/", + "pattern": { + "type": "Pattern", + "parent": "♻️..", + "start": 1, + "end": 34, + "raw": "(?:(?:(?a)|(?b)|c)\\k){2}", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 1, + "end": 34, + "raw": "(?:(?:(?a)|(?b)|c)\\k){2}", + "elements": [ + { + "type": "Quantifier", + "parent": "♻️../..", + "start": 1, + "end": 34, + "raw": "(?:(?:(?a)|(?b)|c)\\k){2}", + "min": 2, + "max": 2, + "greedy": true, + "element": { + "type": "Group", + "parent": "♻️..", + "start": 1, + "end": 31, + "raw": "(?:(?:(?a)|(?b)|c)\\k)", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 4, + "end": 30, + "raw": "(?:(?a)|(?b)|c)\\k", + "elements": [ + { + "type": "Group", + "parent": "♻️../..", + "start": 4, + "end": 25, + "raw": "(?:(?a)|(?b)|c)", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 7, + "end": 14, + "raw": "(?a)", + "elements": [ + { + "type": "CapturingGroup", + "parent": "♻️../..", + "start": 7, + "end": 14, + "raw": "(?a)", + "name": "x", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 12, + "end": 13, + "raw": "a", + "elements": [ + { + "type": "Character", + "parent": "♻️../..", + "start": 12, + "end": 13, + "raw": "a", + "value": 97 + } + ] + } + ], + "references": [ + "♻️../../../../../1" + ] + } + ] + }, + { + "type": "Alternative", + "parent": "♻️../..", + "start": 15, + "end": 22, + "raw": "(?b)", + "elements": [ + { + "type": "CapturingGroup", + "parent": "♻️../..", + "start": 15, + "end": 22, + "raw": "(?b)", + "name": "x", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 20, + "end": 21, + "raw": "b", + "elements": [ + { + "type": "Character", + "parent": "♻️../..", + "start": 20, + "end": 21, + "raw": "b", + "value": 98 + } + ] + } + ], + "references": [] + } + ] + }, + { + "type": "Alternative", + "parent": "♻️../..", + "start": 23, + "end": 24, + "raw": "c", + "elements": [ + { + "type": "Character", + "parent": "♻️../..", + "start": 23, + "end": 24, + "raw": "c", + "value": 99 + } + ] + } + ] + }, + { + "type": "Backreference", + "parent": "♻️../..", + "start": 25, + "end": 30, + "raw": "\\k", + "ref": "x", + "resolved": "♻️../0/alternatives/0/elements/0" + } + ] + } + ] + } + } + ] + } + ] + }, + "flags": { + "type": "Flags", + "parent": "♻️..", + "start": 35, + "end": 35, + "raw": "", + "global": false, + "ignoreCase": false, + "multiline": false, + "unicode": false, + "sticky": false, + "dotAll": false, + "hasIndices": false, + "unicodeSets": false + } } }, "/(?:(?a)|(?b))\\k/": { - "error": { - "message": "Invalid regular expression: /(?:(?a)|(?b))\\k/: Duplicate capture group name", - "index": 17 + "ast": { + "type": "RegExpLiteral", + "parent": null, + "start": 0, + "end": 26, + "raw": "/(?:(?a)|(?b))\\k/", + "pattern": { + "type": "Pattern", + "parent": "♻️..", + "start": 1, + "end": 25, + "raw": "(?:(?a)|(?b))\\k", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 1, + "end": 25, + "raw": "(?:(?a)|(?b))\\k", + "elements": [ + { + "type": "Group", + "parent": "♻️../..", + "start": 1, + "end": 20, + "raw": "(?:(?a)|(?b))", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 4, + "end": 11, + "raw": "(?a)", + "elements": [ + { + "type": "CapturingGroup", + "parent": "♻️../..", + "start": 4, + "end": 11, + "raw": "(?a)", + "name": "x", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 9, + "end": 10, + "raw": "a", + "elements": [ + { + "type": "Character", + "parent": "♻️../..", + "start": 9, + "end": 10, + "raw": "a", + "value": 97 + } + ] + } + ], + "references": [ + "♻️../../../../../1" + ] + } + ] + }, + { + "type": "Alternative", + "parent": "♻️../..", + "start": 12, + "end": 19, + "raw": "(?b)", + "elements": [ + { + "type": "CapturingGroup", + "parent": "♻️../..", + "start": 12, + "end": 19, + "raw": "(?b)", + "name": "x", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 17, + "end": 18, + "raw": "b", + "elements": [ + { + "type": "Character", + "parent": "♻️../..", + "start": 17, + "end": 18, + "raw": "b", + "value": 98 + } + ] + } + ], + "references": [] + } + ] + } + ] + }, + { + "type": "Backreference", + "parent": "♻️../..", + "start": 20, + "end": 25, + "raw": "\\k", + "ref": "x", + "resolved": "♻️../0/alternatives/0/elements/0" + } + ] + } + ] + }, + "flags": { + "type": "Flags", + "parent": "♻️..", + "start": 26, + "end": 26, + "raw": "", + "global": false, + "ignoreCase": false, + "multiline": false, + "unicode": false, + "sticky": false, + "dotAll": false, + "hasIndices": false, + "unicodeSets": false + } } }, "/(?:(?a)|(?a)(?b))(?:(?c)|(?d))/": { - "error": { - "message": "Invalid regular expression: /(?:(?a)|(?a)(?b))(?:(?c)|(?d))/: Duplicate capture group name", - "index": 24 + "ast": { + "type": "RegExpLiteral", + "parent": null, + "start": 0, + "end": 47, + "raw": "/(?:(?a)|(?a)(?b))(?:(?c)|(?d))/", + "pattern": { + "type": "Pattern", + "parent": "♻️..", + "start": 1, + "end": 46, + "raw": "(?:(?a)|(?a)(?b))(?:(?c)|(?d))", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 1, + "end": 46, + "raw": "(?:(?a)|(?a)(?b))(?:(?c)|(?d))", + "elements": [ + { + "type": "Group", + "parent": "♻️../..", + "start": 1, + "end": 27, + "raw": "(?:(?a)|(?a)(?b))", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 4, + "end": 11, + "raw": "(?a)", + "elements": [ + { + "type": "CapturingGroup", + "parent": "♻️../..", + "start": 4, + "end": 11, + "raw": "(?a)", + "name": "x", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 9, + "end": 10, + "raw": "a", + "elements": [ + { + "type": "Character", + "parent": "♻️../..", + "start": 9, + "end": 10, + "raw": "a", + "value": 97 + } + ] + } + ], + "references": [] + } + ] + }, + { + "type": "Alternative", + "parent": "♻️../..", + "start": 12, + "end": 26, + "raw": "(?a)(?b)", + "elements": [ + { + "type": "CapturingGroup", + "parent": "♻️../..", + "start": 12, + "end": 19, + "raw": "(?a)", + "name": "y", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 17, + "end": 18, + "raw": "a", + "elements": [ + { + "type": "Character", + "parent": "♻️../..", + "start": 17, + "end": 18, + "raw": "a", + "value": 97 + } + ] + } + ], + "references": [] + }, + { + "type": "CapturingGroup", + "parent": "♻️../..", + "start": 19, + "end": 26, + "raw": "(?b)", + "name": "x", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 24, + "end": 25, + "raw": "b", + "elements": [ + { + "type": "Character", + "parent": "♻️../..", + "start": 24, + "end": 25, + "raw": "b", + "value": 98 + } + ] + } + ], + "references": [] + } + ] + } + ] + }, + { + "type": "Group", + "parent": "♻️../..", + "start": 27, + "end": 46, + "raw": "(?:(?c)|(?d))", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 30, + "end": 37, + "raw": "(?c)", + "elements": [ + { + "type": "CapturingGroup", + "parent": "♻️../..", + "start": 30, + "end": 37, + "raw": "(?c)", + "name": "z", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 35, + "end": 36, + "raw": "c", + "elements": [ + { + "type": "Character", + "parent": "♻️../..", + "start": 35, + "end": 36, + "raw": "c", + "value": 99 + } + ] + } + ], + "references": [] + } + ] + }, + { + "type": "Alternative", + "parent": "♻️../..", + "start": 38, + "end": 45, + "raw": "(?d)", + "elements": [ + { + "type": "CapturingGroup", + "parent": "♻️../..", + "start": 38, + "end": 45, + "raw": "(?d)", + "name": "z", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 43, + "end": 44, + "raw": "d", + "elements": [ + { + "type": "Character", + "parent": "♻️../..", + "start": 43, + "end": 44, + "raw": "d", + "value": 100 + } + ] + } + ], + "references": [] + } + ] + } + ] + } + ] + } + ] + }, + "flags": { + "type": "Flags", + "parent": "♻️..", + "start": 47, + "end": 47, + "raw": "", + "global": false, + "ignoreCase": false, + "multiline": false, + "unicode": false, + "sticky": false, + "dotAll": false, + "hasIndices": false, + "unicodeSets": false + } } }, "/(?x)|(?:zy\\k)/": { @@ -157,72 +850,633 @@ } }, "/(?a)|(?b)/": { - "error": { - "message": "Invalid regular expression: /(?a)|(?b)/: Duplicate capture group name", - "index": 14 - } - }, - "/(?a)|(?b)/g": { - "error": { - "message": "Invalid regular expression: /(?a)|(?b)/g: Duplicate capture group name", - "index": 14 - } - }, - "/(?b)|(?a)/": { - "error": { - "message": "Invalid regular expression: /(?b)|(?a)/: Duplicate capture group name", - "index": 14 - } - }, - "/(?b)|(?a)/g": { - "error": { - "message": "Invalid regular expression: /(?b)|(?a)/g: Duplicate capture group name", - "index": 14 - } - }, - "/(?a)(?a)|(?b)(?b)/": { - "error": { - "message": "Invalid regular expression: /(?a)(?a)|(?b)(?b)/: Duplicate capture group name", - "index": 21 - } - }, - "/[ab]/": { "ast": { "type": "RegExpLiteral", "parent": null, "start": 0, - "end": 6, - "raw": "/[ab]/", + "end": 17, + "raw": "/(?a)|(?b)/", "pattern": { "type": "Pattern", "parent": "♻️..", "start": 1, - "end": 5, - "raw": "[ab]", + "end": 16, + "raw": "(?a)|(?b)", "alternatives": [ { "type": "Alternative", "parent": "♻️../..", "start": 1, - "end": 5, - "raw": "[ab]", + "end": 8, + "raw": "(?a)", "elements": [ { - "type": "CharacterClass", + "type": "CapturingGroup", "parent": "♻️../..", "start": 1, - "end": 5, - "raw": "[ab]", - "unicodeSets": false, - "negate": false, - "elements": [ + "end": 8, + "raw": "(?a)", + "name": "x", + "alternatives": [ { - "type": "Character", + "type": "Alternative", "parent": "♻️../..", - "start": 2, - "end": 3, + "start": 6, + "end": 7, "raw": "a", - "value": 97 + "elements": [ + { + "type": "Character", + "parent": "♻️../..", + "start": 6, + "end": 7, + "raw": "a", + "value": 97 + } + ] + } + ], + "references": [] + } + ] + }, + { + "type": "Alternative", + "parent": "♻️../..", + "start": 9, + "end": 16, + "raw": "(?b)", + "elements": [ + { + "type": "CapturingGroup", + "parent": "♻️../..", + "start": 9, + "end": 16, + "raw": "(?b)", + "name": "x", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 14, + "end": 15, + "raw": "b", + "elements": [ + { + "type": "Character", + "parent": "♻️../..", + "start": 14, + "end": 15, + "raw": "b", + "value": 98 + } + ] + } + ], + "references": [] + } + ] + } + ] + }, + "flags": { + "type": "Flags", + "parent": "♻️..", + "start": 17, + "end": 17, + "raw": "", + "global": false, + "ignoreCase": false, + "multiline": false, + "unicode": false, + "sticky": false, + "dotAll": false, + "hasIndices": false, + "unicodeSets": false + } + } + }, + "/(?a)|(?b)/g": { + "ast": { + "type": "RegExpLiteral", + "parent": null, + "start": 0, + "end": 18, + "raw": "/(?a)|(?b)/g", + "pattern": { + "type": "Pattern", + "parent": "♻️..", + "start": 1, + "end": 16, + "raw": "(?a)|(?b)", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 1, + "end": 8, + "raw": "(?a)", + "elements": [ + { + "type": "CapturingGroup", + "parent": "♻️../..", + "start": 1, + "end": 8, + "raw": "(?a)", + "name": "x", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 6, + "end": 7, + "raw": "a", + "elements": [ + { + "type": "Character", + "parent": "♻️../..", + "start": 6, + "end": 7, + "raw": "a", + "value": 97 + } + ] + } + ], + "references": [] + } + ] + }, + { + "type": "Alternative", + "parent": "♻️../..", + "start": 9, + "end": 16, + "raw": "(?b)", + "elements": [ + { + "type": "CapturingGroup", + "parent": "♻️../..", + "start": 9, + "end": 16, + "raw": "(?b)", + "name": "x", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 14, + "end": 15, + "raw": "b", + "elements": [ + { + "type": "Character", + "parent": "♻️../..", + "start": 14, + "end": 15, + "raw": "b", + "value": 98 + } + ] + } + ], + "references": [] + } + ] + } + ] + }, + "flags": { + "type": "Flags", + "parent": "♻️..", + "start": 17, + "end": 18, + "raw": "g", + "global": true, + "ignoreCase": false, + "multiline": false, + "unicode": false, + "sticky": false, + "dotAll": false, + "hasIndices": false, + "unicodeSets": false + } + } + }, + "/(?b)|(?a)/": { + "ast": { + "type": "RegExpLiteral", + "parent": null, + "start": 0, + "end": 17, + "raw": "/(?b)|(?a)/", + "pattern": { + "type": "Pattern", + "parent": "♻️..", + "start": 1, + "end": 16, + "raw": "(?b)|(?a)", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 1, + "end": 8, + "raw": "(?b)", + "elements": [ + { + "type": "CapturingGroup", + "parent": "♻️../..", + "start": 1, + "end": 8, + "raw": "(?b)", + "name": "x", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 6, + "end": 7, + "raw": "b", + "elements": [ + { + "type": "Character", + "parent": "♻️../..", + "start": 6, + "end": 7, + "raw": "b", + "value": 98 + } + ] + } + ], + "references": [] + } + ] + }, + { + "type": "Alternative", + "parent": "♻️../..", + "start": 9, + "end": 16, + "raw": "(?a)", + "elements": [ + { + "type": "CapturingGroup", + "parent": "♻️../..", + "start": 9, + "end": 16, + "raw": "(?a)", + "name": "x", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 14, + "end": 15, + "raw": "a", + "elements": [ + { + "type": "Character", + "parent": "♻️../..", + "start": 14, + "end": 15, + "raw": "a", + "value": 97 + } + ] + } + ], + "references": [] + } + ] + } + ] + }, + "flags": { + "type": "Flags", + "parent": "♻️..", + "start": 17, + "end": 17, + "raw": "", + "global": false, + "ignoreCase": false, + "multiline": false, + "unicode": false, + "sticky": false, + "dotAll": false, + "hasIndices": false, + "unicodeSets": false + } + } + }, + "/(?b)|(?a)/g": { + "ast": { + "type": "RegExpLiteral", + "parent": null, + "start": 0, + "end": 18, + "raw": "/(?b)|(?a)/g", + "pattern": { + "type": "Pattern", + "parent": "♻️..", + "start": 1, + "end": 16, + "raw": "(?b)|(?a)", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 1, + "end": 8, + "raw": "(?b)", + "elements": [ + { + "type": "CapturingGroup", + "parent": "♻️../..", + "start": 1, + "end": 8, + "raw": "(?b)", + "name": "x", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 6, + "end": 7, + "raw": "b", + "elements": [ + { + "type": "Character", + "parent": "♻️../..", + "start": 6, + "end": 7, + "raw": "b", + "value": 98 + } + ] + } + ], + "references": [] + } + ] + }, + { + "type": "Alternative", + "parent": "♻️../..", + "start": 9, + "end": 16, + "raw": "(?a)", + "elements": [ + { + "type": "CapturingGroup", + "parent": "♻️../..", + "start": 9, + "end": 16, + "raw": "(?a)", + "name": "x", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 14, + "end": 15, + "raw": "a", + "elements": [ + { + "type": "Character", + "parent": "♻️../..", + "start": 14, + "end": 15, + "raw": "a", + "value": 97 + } + ] + } + ], + "references": [] + } + ] + } + ] + }, + "flags": { + "type": "Flags", + "parent": "♻️..", + "start": 17, + "end": 18, + "raw": "g", + "global": true, + "ignoreCase": false, + "multiline": false, + "unicode": false, + "sticky": false, + "dotAll": false, + "hasIndices": false, + "unicodeSets": false + } + } + }, + "/(?a)(?a)|(?b)(?b)/": { + "ast": { + "type": "RegExpLiteral", + "parent": null, + "start": 0, + "end": 31, + "raw": "/(?a)(?a)|(?b)(?b)/", + "pattern": { + "type": "Pattern", + "parent": "♻️..", + "start": 1, + "end": 30, + "raw": "(?a)(?a)|(?b)(?b)", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 1, + "end": 15, + "raw": "(?a)(?a)", + "elements": [ + { + "type": "CapturingGroup", + "parent": "♻️../..", + "start": 1, + "end": 8, + "raw": "(?a)", + "name": "y", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 6, + "end": 7, + "raw": "a", + "elements": [ + { + "type": "Character", + "parent": "♻️../..", + "start": 6, + "end": 7, + "raw": "a", + "value": 97 + } + ] + } + ], + "references": [] + }, + { + "type": "CapturingGroup", + "parent": "♻️../..", + "start": 8, + "end": 15, + "raw": "(?a)", + "name": "x", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 13, + "end": 14, + "raw": "a", + "elements": [ + { + "type": "Character", + "parent": "♻️../..", + "start": 13, + "end": 14, + "raw": "a", + "value": 97 + } + ] + } + ], + "references": [] + } + ] + }, + { + "type": "Alternative", + "parent": "♻️../..", + "start": 16, + "end": 30, + "raw": "(?b)(?b)", + "elements": [ + { + "type": "CapturingGroup", + "parent": "♻️../..", + "start": 16, + "end": 23, + "raw": "(?b)", + "name": "x", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 21, + "end": 22, + "raw": "b", + "elements": [ + { + "type": "Character", + "parent": "♻️../..", + "start": 21, + "end": 22, + "raw": "b", + "value": 98 + } + ] + } + ], + "references": [] + }, + { + "type": "CapturingGroup", + "parent": "♻️../..", + "start": 23, + "end": 30, + "raw": "(?b)", + "name": "y", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 28, + "end": 29, + "raw": "b", + "elements": [ + { + "type": "Character", + "parent": "♻️../..", + "start": 28, + "end": 29, + "raw": "b", + "value": 98 + } + ] + } + ], + "references": [] + } + ] + } + ] + }, + "flags": { + "type": "Flags", + "parent": "♻️..", + "start": 31, + "end": 31, + "raw": "", + "global": false, + "ignoreCase": false, + "multiline": false, + "unicode": false, + "sticky": false, + "dotAll": false, + "hasIndices": false, + "unicodeSets": false + } + } + }, + "/[ab]/": { + "ast": { + "type": "RegExpLiteral", + "parent": null, + "start": 0, + "end": 6, + "raw": "/[ab]/", + "pattern": { + "type": "Pattern", + "parent": "♻️..", + "start": 1, + "end": 5, + "raw": "[ab]", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 1, + "end": 5, + "raw": "[ab]", + "elements": [ + { + "type": "CharacterClass", + "parent": "♻️../..", + "start": 1, + "end": 5, + "raw": "[ab]", + "unicodeSets": false, + "negate": false, + "elements": [ + { + "type": "Character", + "parent": "♻️../..", + "start": 2, + "end": 3, + "raw": "a", + "value": 97 }, { "type": "Character", @@ -256,15 +1510,351 @@ } }, "/^(?:(?x)|(?y)|z)\\k$/": { - "error": { - "message": "Invalid regular expression: /^(?:(?x)|(?y)|z)\\k$/: Duplicate capture group name", - "index": 18 + "ast": { + "type": "RegExpLiteral", + "parent": null, + "start": 0, + "end": 30, + "raw": "/^(?:(?x)|(?y)|z)\\k$/", + "pattern": { + "type": "Pattern", + "parent": "♻️..", + "start": 1, + "end": 29, + "raw": "^(?:(?x)|(?y)|z)\\k$", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 1, + "end": 29, + "raw": "^(?:(?x)|(?y)|z)\\k$", + "elements": [ + { + "type": "Assertion", + "parent": "♻️../..", + "start": 1, + "end": 2, + "raw": "^", + "kind": "start" + }, + { + "type": "Group", + "parent": "♻️../..", + "start": 2, + "end": 23, + "raw": "(?:(?x)|(?y)|z)", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 5, + "end": 12, + "raw": "(?x)", + "elements": [ + { + "type": "CapturingGroup", + "parent": "♻️../..", + "start": 5, + "end": 12, + "raw": "(?x)", + "name": "a", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 10, + "end": 11, + "raw": "x", + "elements": [ + { + "type": "Character", + "parent": "♻️../..", + "start": 10, + "end": 11, + "raw": "x", + "value": 120 + } + ] + } + ], + "references": [ + "♻️../../../../../2" + ] + } + ] + }, + { + "type": "Alternative", + "parent": "♻️../..", + "start": 13, + "end": 20, + "raw": "(?y)", + "elements": [ + { + "type": "CapturingGroup", + "parent": "♻️../..", + "start": 13, + "end": 20, + "raw": "(?y)", + "name": "a", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 18, + "end": 19, + "raw": "y", + "elements": [ + { + "type": "Character", + "parent": "♻️../..", + "start": 18, + "end": 19, + "raw": "y", + "value": 121 + } + ] + } + ], + "references": [] + } + ] + }, + { + "type": "Alternative", + "parent": "♻️../..", + "start": 21, + "end": 22, + "raw": "z", + "elements": [ + { + "type": "Character", + "parent": "♻️../..", + "start": 21, + "end": 22, + "raw": "z", + "value": 122 + } + ] + } + ] + }, + { + "type": "Backreference", + "parent": "♻️../..", + "start": 23, + "end": 28, + "raw": "\\k", + "ref": "a", + "resolved": "♻️../1/alternatives/0/elements/0" + }, + { + "type": "Assertion", + "parent": "♻️../..", + "start": 28, + "end": 29, + "raw": "$", + "kind": "end" + } + ] + } + ] + }, + "flags": { + "type": "Flags", + "parent": "♻️..", + "start": 30, + "end": 30, + "raw": "", + "global": false, + "ignoreCase": false, + "multiline": false, + "unicode": false, + "sticky": false, + "dotAll": false, + "hasIndices": false, + "unicodeSets": false + } } }, "/^(?:(?x)|(?y)|z){2}\\k$/": { - "error": { - "message": "Invalid regular expression: /^(?:(?x)|(?y)|z){2}\\k$/: Duplicate capture group name", - "index": 18 + "ast": { + "type": "RegExpLiteral", + "parent": null, + "start": 0, + "end": 33, + "raw": "/^(?:(?x)|(?y)|z){2}\\k$/", + "pattern": { + "type": "Pattern", + "parent": "♻️..", + "start": 1, + "end": 32, + "raw": "^(?:(?x)|(?y)|z){2}\\k$", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 1, + "end": 32, + "raw": "^(?:(?x)|(?y)|z){2}\\k$", + "elements": [ + { + "type": "Assertion", + "parent": "♻️../..", + "start": 1, + "end": 2, + "raw": "^", + "kind": "start" + }, + { + "type": "Quantifier", + "parent": "♻️../..", + "start": 2, + "end": 26, + "raw": "(?:(?x)|(?y)|z){2}", + "min": 2, + "max": 2, + "greedy": true, + "element": { + "type": "Group", + "parent": "♻️..", + "start": 2, + "end": 23, + "raw": "(?:(?x)|(?y)|z)", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 5, + "end": 12, + "raw": "(?x)", + "elements": [ + { + "type": "CapturingGroup", + "parent": "♻️../..", + "start": 5, + "end": 12, + "raw": "(?x)", + "name": "a", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 10, + "end": 11, + "raw": "x", + "elements": [ + { + "type": "Character", + "parent": "♻️../..", + "start": 10, + "end": 11, + "raw": "x", + "value": 120 + } + ] + } + ], + "references": [ + "♻️../../../../../../2" + ] + } + ] + }, + { + "type": "Alternative", + "parent": "♻️../..", + "start": 13, + "end": 20, + "raw": "(?y)", + "elements": [ + { + "type": "CapturingGroup", + "parent": "♻️../..", + "start": 13, + "end": 20, + "raw": "(?y)", + "name": "a", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 18, + "end": 19, + "raw": "y", + "elements": [ + { + "type": "Character", + "parent": "♻️../..", + "start": 18, + "end": 19, + "raw": "y", + "value": 121 + } + ] + } + ], + "references": [] + } + ] + }, + { + "type": "Alternative", + "parent": "♻️../..", + "start": 21, + "end": 22, + "raw": "z", + "elements": [ + { + "type": "Character", + "parent": "♻️../..", + "start": 21, + "end": 22, + "raw": "z", + "value": 122 + } + ] + } + ] + } + }, + { + "type": "Backreference", + "parent": "♻️../..", + "start": 26, + "end": 31, + "raw": "\\k", + "ref": "a", + "resolved": "♻️../1/element/alternatives/0/elements/0" + }, + { + "type": "Assertion", + "parent": "♻️../..", + "start": 31, + "end": 32, + "raw": "$", + "kind": "end" + } + ] + } + ] + }, + "flags": { + "type": "Flags", + "parent": "♻️..", + "start": 33, + "end": 33, + "raw": "", + "global": false, + "ignoreCase": false, + "multiline": false, + "unicode": false, + "sticky": false, + "dotAll": false, + "hasIndices": false, + "unicodeSets": false + } } } }